summaryrefslogtreecommitdiffstats
path: root/lib/dns
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dns')
-rw-r--r--lib/dns/Atffile5
-rw-r--r--lib/dns/Kyuafile4
-rw-r--r--lib/dns/Makefile.in227
-rw-r--r--lib/dns/acache.c1821
-rw-r--r--lib/dns/acl.c728
-rw-r--r--lib/dns/adb.c4823
-rw-r--r--lib/dns/api13
-rw-r--r--lib/dns/badcache.c440
-rw-r--r--lib/dns/byaddr.c315
-rw-r--r--lib/dns/cache.c1575
-rw-r--r--lib/dns/callbacks.c110
-rw-r--r--lib/dns/catz.c1992
-rw-r--r--lib/dns/client.c3243
-rw-r--r--lib/dns/clientinfo.c32
-rw-r--r--lib/dns/compress.c400
-rw-r--r--lib/dns/db.c1132
-rw-r--r--lib/dns/dbiterator.c138
-rw-r--r--lib/dns/dbtable.c279
-rw-r--r--lib/dns/diff.c673
-rw-r--r--lib/dns/dispatch.c3906
-rw-r--r--lib/dns/dlz.c549
-rw-r--r--lib/dns/dns64.c297
-rw-r--r--lib/dns/dnssec.c2241
-rw-r--r--lib/dns/dnstap.c1251
-rw-r--r--lib/dns/dnstap.proto268
-rw-r--r--lib/dns/ds.c151
-rw-r--r--lib/dns/dst_api.c2029
-rw-r--r--lib/dns/dst_gost.h58
-rw-r--r--lib/dns/dst_internal.h306
-rw-r--r--lib/dns/dst_lib.c56
-rw-r--r--lib/dns/dst_openssl.h71
-rw-r--r--lib/dns/dst_parse.c856
-rw-r--r--lib/dns/dst_parse.h142
-rw-r--r--lib/dns/dst_pkcs11.h38
-rw-r--r--lib/dns/dst_result.c110
-rw-r--r--lib/dns/dyndb.c467
-rw-r--r--lib/dns/ecdb.c835
-rw-r--r--lib/dns/fixedname.c39
-rw-r--r--lib/dns/forward.c257
-rw-r--r--lib/dns/gen-unix.h90
-rw-r--r--lib/dns/gen-win32.h273
-rw-r--r--lib/dns/gen.c923
-rw-r--r--lib/dns/geoip.c881
-rw-r--r--lib/dns/gssapi_link.c388
-rw-r--r--lib/dns/gssapictx.c894
-rw-r--r--lib/dns/hmac_link.c1811
-rw-r--r--lib/dns/include/Makefile.in17
-rw-r--r--lib/dns/include/dns/Makefile.in60
-rw-r--r--lib/dns/include/dns/acache.h442
-rw-r--r--lib/dns/include/dns/acl.h282
-rw-r--r--lib/dns/include/dns/adb.h841
-rw-r--r--lib/dns/include/dns/badcache.h151
-rw-r--r--lib/dns/include/dns/bit.h30
-rw-r--r--lib/dns/include/dns/byaddr.h166
-rw-r--r--lib/dns/include/dns/cache.h342
-rw-r--r--lib/dns/include/dns/callbacks.h102
-rw-r--r--lib/dns/include/dns/catz.h469
-rw-r--r--lib/dns/include/dns/cert.h62
-rw-r--r--lib/dns/include/dns/client.h674
-rw-r--r--lib/dns/include/dns/clientinfo.h82
-rw-r--r--lib/dns/include/dns/compress.h291
-rw-r--r--lib/dns/include/dns/db.h1690
-rw-r--r--lib/dns/include/dns/dbiterator.h294
-rw-r--r--lib/dns/include/dns/dbtable.h158
-rw-r--r--lib/dns/include/dns/diff.h279
-rw-r--r--lib/dns/include/dns/dispatch.h614
-rw-r--r--lib/dns/include/dns/dlz.h339
-rw-r--r--lib/dns/include/dns/dlz_dlopen.h172
-rw-r--r--lib/dns/include/dns/dns64.h171
-rw-r--r--lib/dns/include/dns/dnssec.h383
-rw-r--r--lib/dns/include/dns/dnstap.h377
-rw-r--r--lib/dns/include/dns/ds.h52
-rw-r--r--lib/dns/include/dns/dsdigest.h71
-rw-r--r--lib/dns/include/dns/dyndb.h161
-rw-r--r--lib/dns/include/dns/ecdb.h48
-rw-r--r--lib/dns/include/dns/edns.h27
-rw-r--r--lib/dns/include/dns/events.h85
-rw-r--r--lib/dns/include/dns/fixedname.h84
-rw-r--r--lib/dns/include/dns/forward.h139
-rw-r--r--lib/dns/include/dns/geoip.h115
-rw-r--r--lib/dns/include/dns/ipkeylist.h87
-rw-r--r--lib/dns/include/dns/iptable.h72
-rw-r--r--lib/dns/include/dns/journal.h298
-rw-r--r--lib/dns/include/dns/keydata.h51
-rw-r--r--lib/dns/include/dns/keyflags.h47
-rw-r--r--lib/dns/include/dns/keytable.h439
-rw-r--r--lib/dns/include/dns/keyvalues.h113
-rw-r--r--lib/dns/include/dns/lib.h52
-rw-r--r--lib/dns/include/dns/log.h111
-rw-r--r--lib/dns/include/dns/lookup.h130
-rw-r--r--lib/dns/include/dns/master.h382
-rw-r--r--lib/dns/include/dns/masterdump.h428
-rw-r--r--lib/dns/include/dns/message.h1436
-rw-r--r--lib/dns/include/dns/name.h1423
-rw-r--r--lib/dns/include/dns/ncache.h186
-rw-r--r--lib/dns/include/dns/nsec.h111
-rw-r--r--lib/dns/include/dns/nsec3.h271
-rw-r--r--lib/dns/include/dns/nta.h204
-rw-r--r--lib/dns/include/dns/opcode.h44
-rw-r--r--lib/dns/include/dns/order.h92
-rw-r--r--lib/dns/include/dns/peer.h263
-rw-r--r--lib/dns/include/dns/portlist.h100
-rw-r--r--lib/dns/include/dns/private.h67
-rw-r--r--lib/dns/include/dns/rbt.h1144
-rw-r--r--lib/dns/include/dns/rcode.h106
-rw-r--r--lib/dns/include/dns/rdata.h774
-rw-r--r--lib/dns/include/dns/rdataclass.h94
-rw-r--r--lib/dns/include/dns/rdatalist.h123
-rw-r--r--lib/dns/include/dns/rdataset.h710
-rw-r--r--lib/dns/include/dns/rdatasetiter.h163
-rw-r--r--lib/dns/include/dns/rdataslab.h178
-rw-r--r--lib/dns/include/dns/rdatatype.h97
-rw-r--r--lib/dns/include/dns/request.h403
-rw-r--r--lib/dns/include/dns/resolver.h714
-rw-r--r--lib/dns/include/dns/result.h201
-rw-r--r--lib/dns/include/dns/rootns.h38
-rw-r--r--lib/dns/include/dns/rpz.h380
-rw-r--r--lib/dns/include/dns/rriterator.h184
-rw-r--r--lib/dns/include/dns/rrl.h277
-rw-r--r--lib/dns/include/dns/sdb.h214
-rw-r--r--lib/dns/include/dns/sdlz.h374
-rw-r--r--lib/dns/include/dns/secalg.h71
-rw-r--r--lib/dns/include/dns/secproto.h64
-rw-r--r--lib/dns/include/dns/soa.h98
-rw-r--r--lib/dns/include/dns/ssu.h259
-rw-r--r--lib/dns/include/dns/stats.h466
-rw-r--r--lib/dns/include/dns/tcpmsg.h142
-rw-r--r--lib/dns/include/dns/time.h73
-rw-r--r--lib/dns/include/dns/timer.h47
-rw-r--r--lib/dns/include/dns/tkey.h248
-rw-r--r--lib/dns/include/dns/tsec.h131
-rw-r--r--lib/dns/include/dns/tsig.h293
-rw-r--r--lib/dns/include/dns/ttl.h82
-rw-r--r--lib/dns/include/dns/types.h437
-rw-r--r--lib/dns/include/dns/update.h66
-rw-r--r--lib/dns/include/dns/validator.h259
-rw-r--r--lib/dns/include/dns/version.h27
-rw-r--r--lib/dns/include/dns/view.h1353
-rw-r--r--lib/dns/include/dns/xfrin.h112
-rw-r--r--lib/dns/include/dns/zone.h2515
-rw-r--r--lib/dns/include/dns/zonekey.h37
-rw-r--r--lib/dns/include/dns/zt.h234
-rw-r--r--lib/dns/include/dst/Makefile.in34
-rw-r--r--lib/dns/include/dst/dst.h995
-rw-r--r--lib/dns/include/dst/gssapi.h215
-rw-r--r--lib/dns/include/dst/lib.h34
-rw-r--r--lib/dns/include/dst/result.h67
-rw-r--r--lib/dns/ipkeylist.c248
-rw-r--r--lib/dns/iptable.c182
-rw-r--r--lib/dns/journal.c2364
-rw-r--r--lib/dns/key.c187
-rw-r--r--lib/dns/keydata.c84
-rw-r--r--lib/dns/keytable.c768
-rw-r--r--lib/dns/lib.c161
-rw-r--r--lib/dns/log.c100
-rw-r--r--lib/dns/lookup.c491
-rw-r--r--lib/dns/mapapi16
-rw-r--r--lib/dns/master.c3242
-rw-r--r--lib/dns/masterdump.c2127
-rw-r--r--lib/dns/message.c4362
-rw-r--r--lib/dns/name.c2734
-rw-r--r--lib/dns/ncache.c750
-rw-r--r--lib/dns/nsec.c445
-rw-r--r--lib/dns/nsec3.c2113
-rw-r--r--lib/dns/nta.c722
-rw-r--r--lib/dns/openssl_link.c464
-rw-r--r--lib/dns/openssldh_link.c793
-rw-r--r--lib/dns/openssldsa_link.c815
-rw-r--r--lib/dns/opensslecdsa_link.c657
-rw-r--r--lib/dns/openssleddsa_link.c676
-rw-r--r--lib/dns/opensslgost_link.c630
-rw-r--r--lib/dns/opensslrsa_link.c1830
-rw-r--r--lib/dns/order.c163
-rw-r--r--lib/dns/peer.c887
-rw-r--r--lib/dns/pkcs11.c45
-rw-r--r--lib/dns/pkcs11dh_link.c1136
-rw-r--r--lib/dns/pkcs11dsa_link.c1126
-rw-r--r--lib/dns/pkcs11ecdsa_link.c1198
-rw-r--r--lib/dns/pkcs11eddsa_link.c1185
-rw-r--r--lib/dns/pkcs11gost_link.c957
-rw-r--r--lib/dns/pkcs11rsa_link.c2237
-rw-r--r--lib/dns/portlist.c261
-rw-r--r--lib/dns/private.c367
-rw-r--r--lib/dns/rbt.c3672
-rw-r--r--lib/dns/rbtdb.c10431
-rw-r--r--lib/dns/rbtdb.h50
-rw-r--r--lib/dns/rbtdb64.c17
-rw-r--r--lib/dns/rbtdb64.h39
-rw-r--r--lib/dns/rcode.c593
-rw-r--r--lib/dns/rdata.c2432
-rw-r--r--lib/dns/rdata/any_255/tsig_250.c598
-rw-r--r--lib/dns/rdata/any_255/tsig_250.h31
-rw-r--r--lib/dns/rdata/ch_3/a_1.c314
-rw-r--r--lib/dns/rdata/ch_3/a_1.h28
-rw-r--r--lib/dns/rdata/generic/afsdb_18.c304
-rw-r--r--lib/dns/rdata/generic/afsdb_18.h27
-rw-r--r--lib/dns/rdata/generic/avc_258.c163
-rw-r--r--lib/dns/rdata/generic/avc_258.h30
-rw-r--r--lib/dns/rdata/generic/caa_257.c365
-rw-r--r--lib/dns/rdata/generic/caa_257.h26
-rw-r--r--lib/dns/rdata/generic/cdnskey_60.c170
-rw-r--r--lib/dns/rdata/generic/cdnskey_60.h18
-rw-r--r--lib/dns/rdata/generic/cds_59.c174
-rw-r--r--lib/dns/rdata/generic/cds_59.h18
-rw-r--r--lib/dns/rdata/generic/cert_37.c277
-rw-r--r--lib/dns/rdata/generic/cert_37.h27
-rw-r--r--lib/dns/rdata/generic/cname_5.c228
-rw-r--r--lib/dns/rdata/generic/cname_5.h22
-rw-r--r--lib/dns/rdata/generic/csync_62.c269
-rw-r--r--lib/dns/rdata/generic/csync_62.h28
-rw-r--r--lib/dns/rdata/generic/dlv_32769.c170
-rw-r--r--lib/dns/rdata/generic/dlv_32769.h19
-rw-r--r--lib/dns/rdata/generic/dname_39.c228
-rw-r--r--lib/dns/rdata/generic/dname_39.h25
-rw-r--r--lib/dns/rdata/generic/dnskey_48.c172
-rw-r--r--lib/dns/rdata/generic/dnskey_48.h21
-rw-r--r--lib/dns/rdata/generic/doa_259.c358
-rw-r--r--lib/dns/rdata/generic/doa_259.h27
-rw-r--r--lib/dns/rdata/generic/ds_43.c400
-rw-r--r--lib/dns/rdata/generic/ds_43.h28
-rw-r--r--lib/dns/rdata/generic/eui48_108.c210
-rw-r--r--lib/dns/rdata/generic/eui48_108.h21
-rw-r--r--lib/dns/rdata/generic/eui64_109.c215
-rw-r--r--lib/dns/rdata/generic/eui64_109.h21
-rw-r--r--lib/dns/rdata/generic/gpos_27.c247
-rw-r--r--lib/dns/rdata/generic/gpos_27.h30
-rw-r--r--lib/dns/rdata/generic/hinfo_13.c216
-rw-r--r--lib/dns/rdata/generic/hinfo_13.h25
-rw-r--r--lib/dns/rdata/generic/hip_55.c497
-rw-r--r--lib/dns/rdata/generic/hip_55.h41
-rw-r--r--lib/dns/rdata/generic/ipseckey_45.c496
-rw-r--r--lib/dns/rdata/generic/ipseckey_45.h29
-rw-r--r--lib/dns/rdata/generic/isdn_20.c237
-rw-r--r--lib/dns/rdata/generic/isdn_20.h28
-rw-r--r--lib/dns/rdata/generic/key_25.c436
-rw-r--r--lib/dns/rdata/generic/key_25.h30
-rw-r--r--lib/dns/rdata/generic/keydata_65533.c441
-rw-r--r--lib/dns/rdata/generic/keydata_65533.h29
-rw-r--r--lib/dns/rdata/generic/l32_105.c228
-rw-r--r--lib/dns/rdata/generic/l32_105.h22
-rw-r--r--lib/dns/rdata/generic/l64_106.c223
-rw-r--r--lib/dns/rdata/generic/l64_106.h22
-rw-r--r--lib/dns/rdata/generic/loc_29.c811
-rw-r--r--lib/dns/rdata/generic/loc_29.h36
-rw-r--r--lib/dns/rdata/generic/lp_107.c271
-rw-r--r--lib/dns/rdata/generic/lp_107.h23
-rw-r--r--lib/dns/rdata/generic/mb_7.c230
-rw-r--r--lib/dns/rdata/generic/mb_7.h23
-rw-r--r--lib/dns/rdata/generic/md_3.c232
-rw-r--r--lib/dns/rdata/generic/md_3.h24
-rw-r--r--lib/dns/rdata/generic/mf_4.c231
-rw-r--r--lib/dns/rdata/generic/mf_4.h23
-rw-r--r--lib/dns/rdata/generic/mg_8.c226
-rw-r--r--lib/dns/rdata/generic/mg_8.h23
-rw-r--r--lib/dns/rdata/generic/minfo_14.c321
-rw-r--r--lib/dns/rdata/generic/minfo_14.h24
-rw-r--r--lib/dns/rdata/generic/mr_9.c227
-rw-r--r--lib/dns/rdata/generic/mr_9.h23
-rw-r--r--lib/dns/rdata/generic/mx_15.c336
-rw-r--r--lib/dns/rdata/generic/mx_15.h24
-rw-r--r--lib/dns/rdata/generic/naptr_35.c662
-rw-r--r--lib/dns/rdata/generic/naptr_35.h33
-rw-r--r--lib/dns/rdata/generic/nid_104.c223
-rw-r--r--lib/dns/rdata/generic/nid_104.h22
-rw-r--r--lib/dns/rdata/generic/ninfo_56.c191
-rw-r--r--lib/dns/rdata/generic/ninfo_56.h34
-rw-r--r--lib/dns/rdata/generic/ns_2.c247
-rw-r--r--lib/dns/rdata/generic/ns_2.h24
-rw-r--r--lib/dns/rdata/generic/nsec3_50.c402
-rw-r--r--lib/dns/rdata/generic/nsec3_50.h112
-rw-r--r--lib/dns/rdata/generic/nsec3param_51.c313
-rw-r--r--lib/dns/rdata/generic/nsec3param_51.h32
-rw-r--r--lib/dns/rdata/generic/nsec_47.c282
-rw-r--r--lib/dns/rdata/generic/nsec_47.h27
-rw-r--r--lib/dns/rdata/generic/null_10.c183
-rw-r--r--lib/dns/rdata/generic/null_10.h25
-rw-r--r--lib/dns/rdata/generic/nxt_30.c325
-rw-r--r--lib/dns/rdata/generic/nxt_30.h27
-rw-r--r--lib/dns/rdata/generic/openpgpkey_61.c242
-rw-r--r--lib/dns/rdata/generic/openpgpkey_61.h22
-rw-r--r--lib/dns/rdata/generic/opt_41.c413
-rw-r--r--lib/dns/rdata/generic/opt_41.h48
-rw-r--r--lib/dns/rdata/generic/proforma.c183
-rw-r--r--lib/dns/rdata/generic/proforma.h23
-rw-r--r--lib/dns/rdata/generic/ptr_12.c268
-rw-r--r--lib/dns/rdata/generic/ptr_12.h23
-rw-r--r--lib/dns/rdata/generic/rkey_57.c168
-rw-r--r--lib/dns/rdata/generic/rkey_57.h17
-rw-r--r--lib/dns/rdata/generic/rp_17.c312
-rw-r--r--lib/dns/rdata/generic/rp_17.h27
-rw-r--r--lib/dns/rdata/generic/rrsig_46.c613
-rw-r--r--lib/dns/rdata/generic/rrsig_46.h34
-rw-r--r--lib/dns/rdata/generic/rt_21.c307
-rw-r--r--lib/dns/rdata/generic/rt_21.h26
-rw-r--r--lib/dns/rdata/generic/sig_24.c575
-rw-r--r--lib/dns/rdata/generic/sig_24.h35
-rw-r--r--lib/dns/rdata/generic/sink_40.c278
-rw-r--r--lib/dns/rdata/generic/sink_40.h25
-rw-r--r--lib/dns/rdata/generic/smimea_53.c156
-rw-r--r--lib/dns/rdata/generic/smimea_53.h17
-rw-r--r--lib/dns/rdata/generic/soa_6.c438
-rw-r--r--lib/dns/rdata/generic/soa_6.h30
-rw-r--r--lib/dns/rdata/generic/spf_99.c163
-rw-r--r--lib/dns/rdata/generic/spf_99.h34
-rw-r--r--lib/dns/rdata/generic/sshfp_44.c263
-rw-r--r--lib/dns/rdata/generic/sshfp_44.h28
-rw-r--r--lib/dns/rdata/generic/ta_32768.c165
-rw-r--r--lib/dns/rdata/generic/ta_32768.h20
-rw-r--r--lib/dns/rdata/generic/talink_58.c264
-rw-r--r--lib/dns/rdata/generic/talink_58.h24
-rw-r--r--lib/dns/rdata/generic/tkey_249.c556
-rw-r--r--lib/dns/rdata/generic/tkey_249.h34
-rw-r--r--lib/dns/rdata/generic/tlsa_52.c334
-rw-r--r--lib/dns/rdata/generic/tlsa_52.h29
-rw-r--r--lib/dns/rdata/generic/txt_16.c360
-rw-r--r--lib/dns/rdata/generic/txt_16.h45
-rw-r--r--lib/dns/rdata/generic/unspec_103.c187
-rw-r--r--lib/dns/rdata/generic/unspec_103.h24
-rw-r--r--lib/dns/rdata/generic/uri_256.c308
-rw-r--r--lib/dns/rdata/generic/uri_256.h25
-rw-r--r--lib/dns/rdata/generic/x25_19.c214
-rw-r--r--lib/dns/rdata/generic/x25_19.h26
-rw-r--r--lib/dns/rdata/hs_4/a_1.c227
-rw-r--r--lib/dns/rdata/hs_4/a_1.h22
-rw-r--r--lib/dns/rdata/in_1/a6_38.c460
-rw-r--r--lib/dns/rdata/in_1/a6_38.h27
-rw-r--r--lib/dns/rdata/in_1/a_1.c245
-rw-r--r--lib/dns/rdata/in_1/a_1.h22
-rw-r--r--lib/dns/rdata/in_1/aaaa_28.c241
-rw-r--r--lib/dns/rdata/in_1/aaaa_28.h24
-rw-r--r--lib/dns/rdata/in_1/apl_42.c456
-rw-r--r--lib/dns/rdata/in_1/apl_42.h52
-rw-r--r--lib/dns/rdata/in_1/dhcid_49.c232
-rw-r--r--lib/dns/rdata/in_1/dhcid_49.h24
-rw-r--r--lib/dns/rdata/in_1/kx_36.c284
-rw-r--r--lib/dns/rdata/in_1/kx_36.h26
-rw-r--r--lib/dns/rdata/in_1/nsap-ptr_23.c241
-rw-r--r--lib/dns/rdata/in_1/nsap-ptr_23.h25
-rw-r--r--lib/dns/rdata/in_1/nsap_22.c251
-rw-r--r--lib/dns/rdata/in_1/nsap_22.h26
-rw-r--r--lib/dns/rdata/in_1/px_26.c370
-rw-r--r--lib/dns/rdata/in_1/px_26.h27
-rw-r--r--lib/dns/rdata/in_1/srv_33.c394
-rw-r--r--lib/dns/rdata/in_1/srv_33.h27
-rw-r--r--lib/dns/rdata/in_1/wks_11.c412
-rw-r--r--lib/dns/rdata/in_1/wks_11.h25
-rw-r--r--lib/dns/rdata/rdatastructpre.h35
-rw-r--r--lib/dns/rdata/rdatastructsuf.h15
-rw-r--r--lib/dns/rdatalist.c415
-rw-r--r--lib/dns/rdatalist_p.h63
-rw-r--r--lib/dns/rdataset.c834
-rw-r--r--lib/dns/rdatasetiter.c73
-rw-r--r--lib/dns/rdataslab.c1134
-rw-r--r--lib/dns/request.c1604
-rw-r--r--lib/dns/resolver.c10327
-rw-r--r--lib/dns/result.c443
-rw-r--r--lib/dns/rootns.c528
-rw-r--r--lib/dns/rpz.c2413
-rw-r--r--lib/dns/rriterator.c204
-rw-r--r--lib/dns/rrl.c1320
-rw-r--r--lib/dns/sdb.c1593
-rw-r--r--lib/dns/sdlz.c2172
-rw-r--r--lib/dns/soa.c142
-rw-r--r--lib/dns/spnego.asn150
-rw-r--r--lib/dns/spnego.c1825
-rw-r--r--lib/dns/spnego.h65
-rw-r--r--lib/dns/spnego_asn1.c862
-rw-r--r--lib/dns/spnego_asn1.pl193
-rw-r--r--lib/dns/ssu.c711
-rw-r--r--lib/dns/ssu_external.c262
-rw-r--r--lib/dns/stats.c475
-rw-r--r--lib/dns/tcpmsg.c239
-rw-r--r--lib/dns/tests/Atffile34
-rw-r--r--lib/dns/tests/Kdh.+002+18602.key1
-rw-r--r--lib/dns/tests/Krsa.+005+29235.key5
-rw-r--r--lib/dns/tests/Kyuafile33
-rw-r--r--lib/dns/tests/Makefile.in266
-rw-r--r--lib/dns/tests/acl_test.c374
-rw-r--r--lib/dns/tests/db_test.c212
-rw-r--r--lib/dns/tests/dbdiff_test.c166
-rw-r--r--lib/dns/tests/dbiterator_test.c425
-rw-r--r--lib/dns/tests/dbversion_test.c735
-rw-r--r--lib/dns/tests/dh_test.c92
-rw-r--r--lib/dns/tests/dispatch_test.c341
-rw-r--r--lib/dns/tests/dnstap_test.c383
-rw-r--r--lib/dns/tests/dnstest.c596
-rw-r--r--lib/dns/tests/dnstest.h135
-rw-r--r--lib/dns/tests/dst_test.c263
-rw-r--r--lib/dns/tests/geoip_test.c711
-rw-r--r--lib/dns/tests/gost_test.c379
-rw-r--r--lib/dns/tests/keytable_test.c622
-rw-r--r--lib/dns/tests/master_test.c684
-rw-r--r--lib/dns/tests/mkraw.pl24
-rw-r--r--lib/dns/tests/name_test.c772
-rw-r--r--lib/dns/tests/nsec3_test.c207
-rw-r--r--lib/dns/tests/peer_test.c140
-rw-r--r--lib/dns/tests/private_test.c220
-rw-r--r--lib/dns/tests/rbt_serialize_test.c456
-rw-r--r--lib/dns/tests/rbt_test.c1437
-rw-r--r--lib/dns/tests/rdata_test.c1214
-rw-r--r--lib/dns/tests/rdataset_test.c125
-rw-r--r--lib/dns/tests/rdatasetstats_test.c172
-rw-r--r--lib/dns/tests/resolver_test.c228
-rw-r--r--lib/dns/tests/rsa_test.c312
-rw-r--r--lib/dns/tests/sigs_test.c479
-rw-r--r--lib/dns/tests/testdata/db/data.db20
-rw-r--r--lib/dns/tests/testdata/dbiterator/zone1.data30
-rw-r--r--lib/dns/tests/testdata/dbiterator/zone2.data319
-rw-r--r--lib/dns/tests/testdata/diff/zone1.data13
-rw-r--r--lib/dns/tests/testdata/diff/zone2.data14
-rw-r--r--lib/dns/tests/testdata/diff/zone3.data12
-rw-r--r--lib/dns/tests/testdata/dnstap/dnstap.savedbin0 -> 30398 bytes
-rw-r--r--lib/dns/tests/testdata/dnstap/dnstap.text96
-rw-r--r--lib/dns/tests/testdata/dnstap/query.auth4
-rw-r--r--lib/dns/tests/testdata/dnstap/query.recursive4
-rw-r--r--lib/dns/tests/testdata/dnstap/response.auth19
-rw-r--r--lib/dns/tests/testdata/dnstap/response.recursive19
-rw-r--r--lib/dns/tests/testdata/dst/Ktest.+001+00002.key1
-rw-r--r--lib/dns/tests/testdata/dst/Ktest.+001+54622.key1
-rw-r--r--lib/dns/tests/testdata/dst/Ktest.+001+54622.private10
-rw-r--r--lib/dns/tests/testdata/dst/Ktest.+003+23616.key1
-rw-r--r--lib/dns/tests/testdata/dst/Ktest.+003+23616.private7
-rw-r--r--lib/dns/tests/testdata/dst/Ktest.+003+49667.key1
-rw-r--r--lib/dns/tests/testdata/dst/test1.data3077
-rw-r--r--lib/dns/tests/testdata/dst/test1.dsasig3
-rw-r--r--lib/dns/tests/testdata/dst/test1.rsasig5
-rw-r--r--lib/dns/tests/testdata/dst/test2.data3077
-rw-r--r--lib/dns/tests/testdata/master/master1.data11
-rw-r--r--lib/dns/tests/testdata/master/master10.data7
-rw-r--r--lib/dns/tests/testdata/master/master11.data6
-rw-r--r--lib/dns/tests/testdata/master/master12.data.in1
-rw-r--r--lib/dns/tests/testdata/master/master13.data.in1
-rw-r--r--lib/dns/tests/testdata/master/master14.data.in1
-rw-r--r--lib/dns/tests/testdata/master/master15.data1609
-rw-r--r--lib/dns/tests/testdata/master/master16.data1609
-rw-r--r--lib/dns/tests/testdata/master/master17.data14
-rw-r--r--lib/dns/tests/testdata/master/master18.data10
-rw-r--r--lib/dns/tests/testdata/master/master2.data11
-rw-r--r--lib/dns/tests/testdata/master/master3.data11
-rw-r--r--lib/dns/tests/testdata/master/master4.data11
-rw-r--r--lib/dns/tests/testdata/master/master5.data11
-rw-r--r--lib/dns/tests/testdata/master/master6.data33
-rw-r--r--lib/dns/tests/testdata/master/master7.data17
-rw-r--r--lib/dns/tests/testdata/master/master8.data4
-rw-r--r--lib/dns/tests/testdata/master/master9.data4
-rw-r--r--lib/dns/tests/testdata/nsec3/1024.db14
-rw-r--r--lib/dns/tests/testdata/nsec3/2048.db14
-rw-r--r--lib/dns/tests/testdata/nsec3/4096.db14
-rw-r--r--lib/dns/tests/testdata/nsec3/min-1024.db18
-rw-r--r--lib/dns/tests/testdata/nsec3/min-2048.db16
-rw-r--r--lib/dns/tests/testdata/zt/zone1.db20
-rw-r--r--lib/dns/tests/testkeys/Kexample.+008+20386.key5
-rw-r--r--lib/dns/tests/testkeys/Kexample.+008+20386.private13
-rw-r--r--lib/dns/tests/testkeys/Kexample.+008+37464.key5
-rw-r--r--lib/dns/tests/testkeys/Kexample.+008+37464.private13
-rw-r--r--lib/dns/tests/time_test.c213
-rw-r--r--lib/dns/tests/tsig_test.c491
-rw-r--r--lib/dns/tests/update_test.c343
-rw-r--r--lib/dns/tests/zonemgr_test.c253
-rw-r--r--lib/dns/tests/zt_test.c342
-rw-r--r--lib/dns/time.c205
-rw-r--r--lib/dns/timer.c55
-rw-r--r--lib/dns/tkey.c1526
-rw-r--r--lib/dns/tsec.c158
-rw-r--r--lib/dns/tsig.c2019
-rw-r--r--lib/dns/ttl.c222
-rw-r--r--lib/dns/update.c2077
-rw-r--r--lib/dns/validator.c3985
-rw-r--r--lib/dns/version.c23
-rw-r--r--lib/dns/view.c2411
-rw-r--r--lib/dns/win32/DLLMain.c50
-rw-r--r--lib/dns/win32/gen.dsp.in107
-rw-r--r--lib/dns/win32/gen.dsw29
-rw-r--r--lib/dns/win32/gen.mak.in267
-rw-r--r--lib/dns/win32/gen.vcxproj.filters.in27
-rw-r--r--lib/dns/win32/gen.vcxproj.in127
-rw-r--r--lib/dns/win32/gen.vcxproj.user3
-rw-r--r--lib/dns/win32/libdns.def.in1504
-rw-r--r--lib/dns/win32/libdns.dsp.in937
-rw-r--r--lib/dns/win32/libdns.dsw29
-rw-r--r--lib/dns/win32/libdns.mak.in2890
-rw-r--r--lib/dns/win32/libdns.vcxproj.filters.in690
-rw-r--r--lib/dns/win32/libdns.vcxproj.in346
-rw-r--r--lib/dns/win32/libdns.vcxproj.user3
-rw-r--r--lib/dns/win32/version.c23
-rw-r--r--lib/dns/xfrin.c1618
-rw-r--r--lib/dns/zone.c19485
-rw-r--r--lib/dns/zone_p.h46
-rw-r--r--lib/dns/zonekey.c50
-rw-r--r--lib/dns/zt.c601
490 files changed, 240839 insertions, 0 deletions
diff --git a/lib/dns/Atffile b/lib/dns/Atffile
new file mode 100644
index 0000000..1edb838
--- /dev/null
+++ b/lib/dns/Atffile
@@ -0,0 +1,5 @@
+Content-Type: application/X-atf-atffile; version="1"
+
+prop: test-suite = bind9
+
+tp: tests
diff --git a/lib/dns/Kyuafile b/lib/dns/Kyuafile
new file mode 100644
index 0000000..0739e3a
--- /dev/null
+++ b/lib/dns/Kyuafile
@@ -0,0 +1,4 @@
+syntax(2)
+test_suite('bind9')
+
+include('tests/Kyuafile')
diff --git a/lib/dns/Makefile.in b/lib/dns/Makefile.in
new file mode 100644
index 0000000..4a8549e
--- /dev/null
+++ b/lib/dns/Makefile.in
@@ -0,0 +1,227 @@
+# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# See the COPYRIGHT file distributed with this work for additional
+# information regarding copyright ownership.
+
+srcdir = @srcdir@
+VPATH = @srcdir@
+top_srcdir = @top_srcdir@
+
+# Attempt to disable parallel processing.
+.NOTPARALLEL:
+.NO_PARALLEL:
+
+VERSION=@BIND9_VERSION@
+@BIND9_MAJOR@
+
+@LIBDNS_MAPAPI@
+
+@LIBDNS_API@
+
+@BIND9_MAKE_INCLUDES@
+
+USE_ISC_SPNEGO = @USE_ISC_SPNEGO@
+
+CINCLUDES = -I. -I${top_srcdir}/lib/dns -Iinclude ${DNS_INCLUDES} \
+ ${ISC_INCLUDES} @DST_OPENSSL_INC@ @DST_GSSAPI_INC@
+
+CDEFINES = -DUSE_MD5 @CRYPTO@ @USE_GSSAPI@ ${USE_ISC_SPNEGO}
+
+CWARNINGS =
+
+ISCLIBS = ../../lib/isc/libisc.@A@
+
+ISCDEPLIBS = ../../lib/isc/libisc.@A@
+
+LIBS = @LIBS@
+
+# Alphabetically
+
+OPENSSLGOSTLINKOBJS = opensslgost_link.@O@
+OPENSSLECDSALINKOBJS = opensslecdsa_link.@O@
+OPENSSLEDDSALINKOBJS = openssleddsa_link.@O@
+OPENSSLLINKOBJS = openssl_link.@O@ openssldh_link.@O@ openssldsa_link.@O@ \
+ @OPENSSLECDSALINKOBJS@ @OPENSSLEDDSALINKOBJS@ \
+ @OPENSSLGOSTLINKOBJS@ opensslrsa_link.@O@
+
+PKCS11LINKOBJS = pkcs11dh_link.@O@ pkcs11dsa_link.@O@ pkcs11rsa_link.@O@ \
+ pkcs11ecdsa_link.@O@ pkcs11eddsa_link.@O@ \
+ pkcs11gost_link.@O@ pkcs11.@O@
+
+DSTOBJS = @DST_EXTRA_OBJS@ @OPENSSLLINKOBJS@ @PKCS11LINKOBJS@ \
+ dst_api.@O@ dst_lib.@O@ dst_parse.@O@ dst_result.@O@ \
+ gssapi_link.@O@ gssapictx.@O@ hmac_link.@O@ key.@O@
+
+GEOIPLINKOBJS = geoip.@O@
+
+DNSTAPOBJS = dnstap.@O@ dnstap.pb-c.@O@
+
+# Alphabetically
+DNSOBJS = acache.@O@ acl.@O@ adb.@O@ badcache.@O@ byaddr.@O@ \
+ cache.@O@ callbacks.@O@ catz.@O@ clientinfo.@O@ compress.@O@ \
+ db.@O@ dbiterator.@O@ dbtable.@O@ diff.@O@ dispatch.@O@ \
+ dlz.@O@ dns64.@O@ dnssec.@O@ ds.@O@ dyndb.@O@ \
+ fixedname.@O@ forward.@O@ \
+ ipkeylist.@O@ iptable.@O@ journal.@O@ keydata.@O@ \
+ keytable.@O@ lib.@O@ log.@O@ lookup.@O@ \
+ master.@O@ masterdump.@O@ message.@O@ \
+ name.@O@ ncache.@O@ nsec.@O@ nsec3.@O@ nta.@O@ \
+ order.@O@ peer.@O@ portlist.@O@ private.@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@ \
+ rpz.@O@ rrl.@O@ rriterator.@O@ sdb.@O@ \
+ sdlz.@O@ soa.@O@ ssu.@O@ ssu_external.@O@ \
+ stats.@O@ tcpmsg.@O@ time.@O@ timer.@O@ tkey.@O@ \
+ tsec.@O@ tsig.@O@ ttl.@O@ update.@O@ validator.@O@ \
+ version.@O@ view.@O@ xfrin.@O@ zone.@O@ zonekey.@O@ zt.@O@
+PORTDNSOBJS = client.@O@ ecdb.@O@
+
+OBJS= @DNSTAPOBJS@ ${DNSOBJS} ${OTHEROBJS} ${DSTOBJS} \
+ ${PORTDNSOBJS} @GEOIPLINKOBJS@
+
+
+# Alphabetically
+OPENSSLGOSTLINKSRCS = opensslgost_link.c
+OPENSSLECDSALINKSRCS = opensslecdsa_link.c
+OPENSSLEDDSALINKSRCS = openssleddsa_link.c
+OPENSSLLINKSRCS = openssl_link.c openssldh_link.c openssldsa_link.c \
+ @OPENSSLECDSALINKSRCS@ @OPENSSLEDDSALINKSRCS@ \
+ @OPENSSLGOSTLINKSRCS@ opensslrsa_link.c
+
+PKCS11LINKSRCS = pkcs11dh_link.c pkcs11dsa_link.c pkcs11rsa_link.c \
+ pkcs11ecdsa_link.c pkcs11eddsa_link.c \
+ pkcs11gost_link.c pkcs11.c
+
+DSTSRCS = @DST_EXTRA_SRCS@ @OPENSSLLINKSRCS@ @PKCS11LINKSRCS@ \
+ dst_api.c dst_lib.c dst_parse.c \
+ dst_result.c gssapi_link.c gssapictx.c \
+ hmac_link.c key.c
+
+GEOIPLINKSRCS = geoip.c
+
+DNSTAPSRCS = dnstap.c dnstap.pb-c.c
+
+DNSSRCS = acache.c acl.c adb.c badcache. byaddr.c \
+ cache.c callbacks.c clientinfo.c compress.c \
+ db.c dbiterator.c dbtable.c diff.c dispatch.c \
+ dlz.c dns64.c dnssec.c ds.c dyndb.c fixedname.c forward.c \
+ ipkeylist.c iptable.c journal.c keydata.c keytable.c lib.c \
+ log.c lookup.c master.c masterdump.c message.c \
+ name.c ncache.c nsec.c nsec3.c nta.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 rpz.c rrl.c rriterator.c \
+ sdb.c sdlz.c soa.c ssu.c ssu_external.c \
+ stats.c tcpmsg.c time.c timer.c tkey.c \
+ tsec.c tsig.c ttl.c update.c validator.c \
+ version.c view.c xfrin.c zone.c zonekey.c zt.c ${OTHERSRCS}
+PORTDNSSRCS = client.c ecdb.c
+
+SRCS = ${DSTSRCS} ${DNSSRCS} ${PORTDNSSRCS} @DNSTAPSRCS@ @GEOIPLINKSRCS@
+
+SUBDIRS = include
+TARGETS = timestamp
+TESTDIRS = @UNITTESTS@
+
+DEPENDEXTRA = ./gen -F include/dns/rdatastruct.h \
+ -s ${srcdir} -d >> Makefile ;
+
+@BIND9_MAKE_RULES@
+
+PROTOC_C = @PROTOC_C@
+
+version.@O@: version.c
+ ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} \
+ -DVERSION=\"${VERSION}\" \
+ -DMAJOR=\"${MAJOR}\" \
+ -DMAPAPI=\"${MAPAPI}\" \
+ -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}
+
+include: gen
+ ${MAKE} include/dns/enumtype.h
+ ${MAKE} include/dns/enumclass.h
+ ${MAKE} include/dns/rdatastruct.h
+ ${MAKE} code.h
+
+include/dns/enumtype.h: gen
+ ./gen -s ${srcdir} -t > $@ || { rm -f $@ ; exit 1; }
+
+include/dns/enumclass.h: gen
+ ./gen -s ${srcdir} -c > $@ || { rm -f $@ ; exit 1; }
+
+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 > $@ || \
+ { rm -f $@ ; exit 1; }
+
+code.h: gen
+ ./gen -s ${srcdir} > code.h || { rm -f $@ ; exit 1; }
+
+gen: gen.c
+ ${BUILD_CC} ${BUILD_CFLAGS} -I${top_srcdir}/lib/isc/include \
+ ${BUILD_CPPFLAGS} ${BUILD_LDFLAGS} -o $@ ${srcdir}/gen.c ${BUILD_LIBS}
+
+timestamp: include libdns.@A@
+ touch timestamp
+
+testdirs: libdns.@A@
+
+installdirs:
+ $(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${libdir}
+
+install:: timestamp installdirs
+ ${LIBTOOL_MODE_INSTALL} ${INSTALL_LIBRARY} libdns.@A@ ${DESTDIR}${libdir}
+
+uninstall::
+ ${LIBTOOL_MODE_UNINSTALL} rm -f ${DESTDIR}${libdir}/libdns.@A@
+
+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
+ rm -f dnstap.pb-c.c dnstap.pb-c.h include/dns/dnstap.pb-c.h
+
+newrr::
+ rm -f code.h include/dns/enumtype.h include/dns/enumclass.h
+ rm -f include/dns/rdatastruct.h
+
+rdata.@O@: include
+
+rbtdb64.@O@: rbtdb64.c rbtdb.c
+
+depend: include
+subdirs: include
+${OBJS}: include
+
+# dnstap
+dnstap.@O@: dnstap.c dnstap.pb-c.c
+
+dnstap.pb-c.c dnstap.pb-c.h include/dns/dnstap.pb-c.h: dnstap.proto
+ $(PROTOC_C) --c_out=. dnstap.proto
+ cp -f dnstap.pb-c.h include/dns
+
+dnstap.pb-c.@O@: dnstap.pb-c.c
+
+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..4e5fd40
--- /dev/null
+++ b/lib/dns/acache.c
@@ -0,0 +1,1821 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $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/platform.h>
+#include <isc/random.h>
+#include <isc/refcount.h>
+#include <isc/rwlock.h>
+#include <isc/serial.h>
+#include <isc/task.h>
+#include <isc/time.h>
+#include <isc/timer.h>
+#include <isc/util.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>
+
+#if defined(ISC_PLATFORM_HAVESTDATOMIC)
+#if defined(__cplusplus)
+#include <isc/stdatomic.h>
+#else
+#include <stdatomic.h>
+#endif
+#endif
+
+#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 2097152U /* 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_HAVESTDATOMIC) && defined(ATOMIC_LONG_LOCK_FREE)) || \
+ defined(ISC_PLATFORM_HAVEATOMICSTORE))
+#define ACACHE_USE_RWLOCK 1
+#if (defined(ISC_PLATFORM_HAVESTDATOMIC) && defined(ATOMIC_LONG_LOCK_FREE))
+#define ACACHE_HAVESTDATOMIC 1
+#endif
+#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))
+
+#ifdef ACACHE_HAVESTDATOMIC
+#define acache_storetime(entry, t) \
+ atomic_store_explicit(&(entry)->lastused, (t), \
+ memory_order_relaxed);
+#else
+#define acache_storetime(entry, t) \
+ (isc_atomic_store((int32_t *)&(entry)->lastused, (t)))
+#endif
+
+#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. */
+ bool 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];
+
+ bool shutting_down;
+
+ isc_task_t *task;
+ isc_event_t cevent;
+ bool 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 */
+#ifdef ACACHE_HAVESTDATOMIC
+ atomic_uint_fast32_t lastused;
+#else
+ isc_stdtime32_t lastused;
+#endif
+};
+
+/*
+ * Internal functions (and prototypes).
+ */
+static inline bool 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 bool
+check_noentry(dns_acache_t *acache) {
+ if (ISC_LIST_EMPTY(acache->entries) && acache->dbentries == 0) {
+ return (true);
+ }
+
+ return (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_function(&db, sizeof(db), true, NULL) % 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, 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 = 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 bool
+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 (true);
+
+ if (interval > cleaner->cleaning_interval)
+ interval = cleaner->cleaning_interval;
+
+ if (entry->lastused + interval < now32)
+ return (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;
+ uint32_t val;
+
+ if (isc_serial_ge(now32, entry->lastused))
+ passed = now32 - entry->lastused; /* <= interval */
+ else
+ passed = 0;
+
+ if (passed > interval / 2)
+ return (true);
+ isc_random_get(&val);
+ if (passed > interval / 4)
+ return (val % 4 == 0);
+ return (val % 8 == 0);
+ }
+
+ return (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);
+ if (isc_serial_ge(now32, last32))
+ interval = now32 - last32;
+ else
+ interval = 0;
+
+ while (n_entries-- > 0) {
+ bool is_stale = 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. In case we
+ * exit from the loop immediately after
+ * this, reset next to the head entry
+ * as we'll expect it will be never
+ * NULL.
+ */
+ isc_log_write(dns_lctx,
+ DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_ACACHE,
+ ISC_LOG_DEBUG(1),
+ "acache cleaner: "
+ "still overmem, "
+ "reset and try again");
+ next = entry;
+ 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);
+ 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;
+ bool want_cleaning = 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 = 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;
+ bool overmem = (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;
+ bool should_free = 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) == true);
+ should_free = 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 = 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 = 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;
+ bool should_free = 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) == true);
+ should_free = 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 = 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 = 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 = 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_function(&db, sizeof(db), true, NULL) % 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_function(&db, sizeof(db), true, NULL) % 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;
+ uint32_t r;
+ isc_stdtime_t tmptime;
+
+ 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(&tmptime);
+ acache_storetime(newentry, tmptime);
+
+ 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_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, 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;
+ bool close_version = 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 = true;
+ }
+ }
+ if (version != NULL) {
+ INSIST(db != NULL);
+ dns_db_attachversion(db, version, &entry->version);
+ }
+ if (close_version)
+ dns_db_closeversion(db, &version, 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);
+}
+
+bool
+dns_acache_cancelentry(dns_acacheentry_t *entry) {
+ dns_acache_t *acache;
+ bool callback_active;
+
+ REQUIRE(DNS_ACACHEENTRY_VALID(entry));
+
+ acache = entry->acache;
+
+ INSIST(DNS_ACACHE_VALID(entry->acache));
+
+ LOCK(&acache->lock);
+ ACACHE_LOCK(&acache->entrylocks[entry->locknum], isc_rwlocktype_write);
+
+ callback_active = (entry->cbarg != NULL);
+
+ /*
+ * 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);
+
+ return (callback_active);
+}
+
+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, true);
+ } else {
+ isc_interval_set(&interval, acache->cleaner.cleaning_interval,
+ 0);
+ result = isc_timer_reset(acache->cleaner.cleaning_timer,
+ isc_timertype_ticker,
+ NULL, &interval, 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, size_t size) {
+ size_t hiwater, lowater;
+
+ REQUIRE(DNS_ACACHE_VALID(acache));
+
+ if (size != 0U && size < DNS_ACACHE_MINSIZE)
+ size = DNS_ACACHE_MINSIZE;
+
+ hiwater = size - (size >> 3);
+ lowater = size - (size >> 2);
+
+ if (size == 0U || hiwater == 0U || lowater == 0U)
+ 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..c1108e8
--- /dev/null
+++ b/lib/dns/acl.c
@@ -0,0 +1,728 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdbool.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 = NULL;
+ isc_mem_attach(mctx, &acl->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 = 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, bool 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, !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, 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, true, target));
+}
+
+/*
+ * If pos is true, test whether acl is set to "{ any; }"
+ * If pos is false, test whether acl is set to "{ none; }"
+ */
+static bool
+dns_acl_isanyornone(dns_acl_t *acl, bool 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 (false);
+
+ if (acl->length != 0 || acl->node_count != 1)
+ return (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] &&
+ *(bool *) (acl->iptable->radix->head->data[0]) == pos)
+ return (true);
+
+ return (false); /* All others */
+}
+
+/*
+ * Test whether acl is set to "{ any; }"
+ */
+bool
+dns_acl_isany(dns_acl_t *acl)
+{
+ return (dns_acl_isanyornone(acl, true));
+}
+
+/*
+ * Test whether acl is set to "{ none; }"
+ */
+bool
+dns_acl_isnone(dns_acl_t *acl)
+{
+ return (dns_acl_isanyornone(acl, 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)
+{
+ return (dns_acl_match2(reqaddr, reqsigner, NULL, 0, NULL, acl, env,
+ match, matchelt));
+}
+
+isc_result_t
+dns_acl_match2(const isc_netaddr_t *reqaddr,
+ const dns_name_t *reqsigner,
+ const isc_netaddr_t *ecs,
+ uint8_t ecslen,
+ uint8_t *scope,
+ const dns_acl_t *acl,
+ const dns_aclenv_t *env,
+ int *match,
+ const dns_aclelement_t **matchelt)
+{
+ uint16_t bitlen;
+ isc_prefix_t pfx;
+ isc_radix_node_t *node = NULL;
+ const isc_netaddr_t *addr = reqaddr;
+ isc_netaddr_t v4addr;
+ isc_result_t result;
+ int match_num = -1;
+ unsigned int i;
+
+ REQUIRE(reqaddr != NULL);
+ REQUIRE(matchelt == NULL || *matchelt == NULL);
+ REQUIRE(ecs != NULL || scope == NULL);
+
+ if (env != NULL && env->match_mapped &&
+ addr->family == AF_INET6 &&
+ IN6_IS_ADDR_V4MAPPED(&addr->type.in6))
+ {
+ isc_netaddr_fromv4mapped(&v4addr, addr);
+ addr = &v4addr;
+ }
+
+ /* Always match with host addresses. */
+ bitlen = (addr->family == AF_INET6) ? 128 : 32;
+ NETADDR_TO_PREFIX_T(addr, pfx, bitlen, false);
+
+ /* 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) {
+ int fam = ISC_RADIX_FAMILY(&pfx);
+ match_num = node->node_num[fam];
+ if (*(bool *) node->data[fam]) {
+ *match = match_num;
+ } else {
+ *match = -match_num;
+ }
+ }
+
+ isc_refcount_destroy(&pfx.refcount);
+
+ /*
+ * If ecs is not NULL, we search the radix tree again to
+ * see if we find a better match on an ECS node
+ */
+ if (ecs != NULL) {
+ node = NULL;
+ addr = ecs;
+
+ if (env != NULL && env->match_mapped &&
+ addr->family == AF_INET6 &&
+ IN6_IS_ADDR_V4MAPPED(&addr->type.in6))
+ {
+ isc_netaddr_fromv4mapped(&v4addr, addr);
+ addr = &v4addr;
+ }
+
+ NETADDR_TO_PREFIX_T(addr, pfx, ecslen, true);
+
+ result = isc_radix_search(acl->iptable->radix, &node, &pfx);
+ if (result == ISC_R_SUCCESS && node != NULL) {
+ int off = ISC_RADIX_FAMILY(&pfx);
+ if (match_num == -1 ||
+ node->node_num[off] < match_num)
+ {
+ match_num = node->node_num[off];
+ if (scope != NULL) {
+ *scope = node->bit;
+ }
+ if (*(bool *) node->data[off]) {
+ *match = match_num;
+ } else {
+ *match = -match_num;
+ }
+ }
+ }
+
+ isc_refcount_destroy(&pfx.refcount);
+ }
+
+ /* 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) {
+ break;
+ }
+
+ if (dns_aclelement_match2(reqaddr, reqsigner, ecs, ecslen,
+ scope, e, env, matchelt))
+ {
+ if (match_num == -1 || e->node_num < match_num) {
+ if (e->negative)
+ *match = -e->node_num;
+ else
+ *match = e->node_num;
+ }
+ break;
+ }
+ }
+
+ 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 positive match in the parent ACL.
+ */
+isc_result_t
+dns_acl_merge(dns_acl_t *dest, dns_acl_t *source, bool 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);
+
+ /* Zero. */
+ memset(newmem, 0, newalloc * sizeof(dns_aclelement_t));
+
+ /* Copy in the original elements */
+ memmove(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;
+ }
+
+#ifdef HAVE_GEOIP
+ /* Duplicate GeoIP data */
+ if (source->elements[i].type == dns_aclelementtype_geoip) {
+ dest->elements[nelem + i].geoip_elem =
+ source->elements[i].geoip_elem;
+ }
+#endif
+
+ /* reverse sense of positives if this is a negative acl */
+ if (!pos && !source->elements[i].negative) {
+ dest->elements[nelem + i].negative = 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 true iff it matched.
+ *
+ * To determine whether the match was positive 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.
+ */
+bool
+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)
+{
+ return (dns_aclelement_match2(reqaddr, reqsigner, NULL, 0, NULL,
+ e, env, matchelt));
+}
+
+bool
+dns_aclelement_match2(const isc_netaddr_t *reqaddr,
+ const dns_name_t *reqsigner,
+ const isc_netaddr_t *ecs,
+ uint8_t ecslen,
+ uint8_t *scope,
+ 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;
+#ifdef HAVE_GEOIP
+ const isc_netaddr_t *addr = NULL;
+#endif
+
+ REQUIRE(ecs != NULL || scope == NULL);
+
+ switch (e->type) {
+ case dns_aclelementtype_keyname:
+ if (reqsigner != NULL &&
+ dns_name_equal(reqsigner, &e->keyname)) {
+ if (matchelt != NULL)
+ *matchelt = e;
+ return (true);
+ } else
+ return (false);
+
+ case dns_aclelementtype_nestedacl:
+ inner = e->nestedacl;
+ break;
+
+ case dns_aclelementtype_localhost:
+ if (env == NULL || env->localhost == NULL)
+ return (false);
+ inner = env->localhost;
+ break;
+
+ case dns_aclelementtype_localnets:
+ if (env == NULL || env->localnets == NULL)
+ return (false);
+ inner = env->localnets;
+ break;
+
+#ifdef HAVE_GEOIP
+ case dns_aclelementtype_geoip:
+ if (env == NULL || env->geoip == NULL)
+ return (false);
+ addr = (env->geoip_use_ecs && ecs != NULL) ? ecs : reqaddr;
+ return (dns_geoip_match(addr, scope, env->geoip,
+ &e->geoip_elem));
+#endif
+ default:
+ /* Should be impossible. */
+ INSIST(0);
+ }
+
+ result = dns_acl_match2(reqaddr, reqsigner, ecs, ecslen, scope,
+ 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 (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 (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;
+
+ INSIST(!ISC_LINK_LINKED(dacl, nextincache));
+
+ 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_putanddetach(&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 bool insecure_prefix_found;
+
+static void
+initialize_action(void) {
+ RUNTIME_CHECK(isc_mutex_init(&insecure_prefix_lock) == ISC_R_SUCCESS);
+}
+
+/*
+ * Called via isc_radix_process() to find IP table nodes that are
+ * insecure.
+ */
+static void
+is_insecure(isc_prefix_t *prefix, void **data) {
+ /*
+ * If all nonexistent or negative then this node is secure.
+ */
+ if ((data[0] == NULL || !* (bool *) data[0]) &&
+ (data[1] == NULL || !* (bool *) data[1]) &&
+ (data[2] == NULL || !* (bool *) data[2]) &&
+ (data[3] == NULL || !* (bool *) data[3]))
+ return;
+
+ /*
+ * If a loopback address found and the other family and
+ * ecs entry doesn't exist or is negative, return.
+ */
+ if (prefix->bitlen == 32 &&
+ htonl(prefix->add.sin.s_addr) == INADDR_LOOPBACK &&
+ (data[1] == NULL || !* (bool *) data[1]) &&
+ (data[2] == NULL || !* (bool *) data[2]) &&
+ (data[3] == NULL || !* (bool *) data[3]))
+ return;
+
+ if (prefix->bitlen == 128 &&
+ IN6_IS_ADDR_LOOPBACK(&prefix->add.sin6) &&
+ (data[0] == NULL || !* (bool *) data[0]) &&
+ (data[2] == NULL || !* (bool *) data[2]) &&
+ (data[3] == NULL || !* (bool *) data[3]))
+ return;
+
+ /* Non-negated, non-loopback */
+ insecure_prefix_found = true; /* LOCKED */
+ return;
+}
+
+/*
+ * Return 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 false is safe.
+ */
+bool
+dns_acl_isinsecure(const dns_acl_t *a) {
+ unsigned int i;
+ bool 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 = false;
+ isc_radix_process(a->iptable->radix, is_insecure);
+ insecure = insecure_prefix_found;
+ UNLOCK(&insecure_prefix_lock);
+ if (insecure)
+ return (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 (true);
+ continue;
+
+#ifdef HAVE_GEOIP
+ case dns_aclelementtype_geoip:
+#endif
+ case dns_aclelementtype_localnets:
+ return (true);
+
+ default:
+ INSIST(0);
+ return (true);
+ }
+ }
+
+ /* No insecure elements were found. */
+ return (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 = false;
+#ifdef HAVE_GEOIP
+ env->geoip = NULL;
+ env->geoip_use_ecs = false;
+#endif
+ 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;
+#ifdef HAVE_GEOIP
+ t->geoip_use_ecs = s->geoip_use_ecs;
+#endif
+}
+
+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..956f368
--- /dev/null
+++ b/lib/dns/adb.c
@@ -0,0 +1,4823 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \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 <stdbool.h>
+#include <limits.h>
+#include <inttypes.h>
+
+#include <isc/mutexblock.h>
+#include <isc/netaddr.h>
+#include <isc/print.h>
+#include <isc/random.h>
+#include <isc/stats.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)
+
+/*!
+ * 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 (1024U*1024U) /*%< 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_task_t *excl;
+
+ 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?
+ */
+ unsigned int nnames;
+ isc_mutex_t namescntlock;
+ unsigned int namescnt;
+ dns_adbnamelist_t *names;
+ dns_adbnamelist_t *deadnames;
+ isc_mutex_t *namelocks;
+ bool *name_sd;
+ unsigned int *name_refcnt;
+
+ /*!
+ * Bucketized locks and lists for entries.
+ *
+ * XXXRTH Have a per-bucket structure that contains all of these?
+ */
+ unsigned int nentries;
+ isc_mutex_t entriescntlock;
+ unsigned int entriescnt;
+ dns_adbentrylist_t *entries;
+ dns_adbentrylist_t *deadentries;
+ isc_mutex_t *entrylocks;
+ bool *entry_sd; /*%< shutting down */
+ unsigned int *entry_refcnt;
+
+ isc_event_t cevent;
+ bool cevent_out;
+ bool shutting_down;
+ isc_eventlist_t whenshutdown;
+ isc_event_t growentries;
+ bool growentries_sent;
+ isc_event_t grownames;
+ bool grownames_sent;
+
+ uint32_t quota;
+ uint32_t atr_freq;
+ double atr_low;
+ double atr_high;
+ double atr_discount;
+};
+
+/*
+ * 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_fetch_t *fetch;
+ dns_rdataset_t rdataset;
+ unsigned int depth;
+};
+
+/*%
+ * 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 nh;
+
+ unsigned int flags;
+ unsigned int srtt;
+ uint16_t udpsize;
+ unsigned int completed;
+ unsigned int timeouts;
+ unsigned char plain;
+ unsigned char plainto;
+ unsigned char edns;
+ unsigned char to4096; /* Our max. */
+
+ uint8_t mode;
+ uint32_t quota;
+ uint32_t active;
+ double atr;
+
+ /*
+ * Allow for encapsulated IPv4/IPv6 UDP packet over ethernet.
+ * Ethernet 1500 - IP(20) - IP6(40) - UDP(8) = 1432.
+ */
+ unsigned char to1432; /* Ethernet */
+ unsigned char to1232; /* IPv6 nofrag */
+ unsigned char to512; /* plain DNS */
+ isc_sockaddr_t sockaddr;
+ unsigned char * cookie;
+ uint16_t cookielen;
+
+ isc_stdtime_t expires;
+ isc_stdtime_t lastage;
+ /*%<
+ * 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 bool 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 *,
+ isc_stdtime_t);
+static void dump_adb(dns_adb_t *, FILE *, bool debug, isc_stdtime_t);
+static void print_dns_name(FILE *, dns_name_t *);
+static void print_namehook_list(FILE *, const char *legend,
+ dns_adb_t *adb,
+ dns_adbnamehooklist_t *list,
+ bool 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 bool 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 *,
+ bool);
+static inline bool dec_entry_refcnt(dns_adb_t *, bool,
+ dns_adbentry_t *, bool);
+static inline void violate_locking_hierarchy(isc_mutex_t *, isc_mutex_t *);
+static bool clean_namehooks(dns_adb_t *, dns_adbnamehooklist_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 bool check_expire_namehooks(dns_adbname_t *, isc_stdtime_t);
+static bool check_expire_entry(dns_adb_t *, dns_adbentry_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 *, bool,
+ unsigned int, isc_counter_t *qc,
+ dns_rdatatype_t);
+static inline void check_exit(dns_adb_t *);
+static void destroy(dns_adb_t *);
+static bool shutdown_names(dns_adb_t *);
+static bool shutdown_entries(dns_adb_t *);
+static inline void link_name(dns_adb_t *, int, dns_adbname_t *);
+static inline bool unlink_name(dns_adb_t *, dns_adbname_t *);
+static inline void link_entry(dns_adb_t *, int, dns_adbentry_t *);
+static inline bool unlink_entry(dns_adb_t *, dns_adbentry_t *);
+static bool kill_name(dns_adbname_t **, isc_eventtype_t);
+static void water(void *, int);
+static void dump_entry(FILE *, dns_adb_t *, dns_adbentry_t *,
+ bool, isc_stdtime_t);
+static void adjustsrtt(dns_adbaddrinfo_t *addr, unsigned int rtt,
+ unsigned int factor, isc_stdtime_t now);
+static void shutdown_task(isc_task_t *task, isc_event_t *ev);
+static void log_quota(dns_adbentry_t *entry, const char *fmt, ...)
+ ISC_FORMAT_PRINTF(2, 3);
+
+/*
+ * 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)
+
+/*
+ * Private flag(s) for entries.
+ * MUST NOT overlap FCTX_ADDRINFO_xxx and DNS_FETCHOPT_NOEDNS0.
+ */
+#define ENTRY_IS_DEAD 0x00400000
+
+/*
+ * 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, isc_statscounter_t counter) {
+ if (adb->view->resstats != NULL)
+ isc_stats_increment(adb->view->resstats, counter);
+}
+
+/*%
+ * Set adb-related statistics counters.
+ */
+static inline void
+set_adbstat(dns_adb_t *adb, uint64_t val, isc_statscounter_t counter) {
+ if (adb->view->adbstats != NULL)
+ isc_stats_set(adb->view->adbstats, val, counter);
+}
+
+static inline void
+dec_adbstats(dns_adb_t *adb, isc_statscounter_t counter) {
+ if (adb->view->adbstats != NULL)
+ isc_stats_decrement(adb->view->adbstats, counter);
+}
+
+static inline void
+inc_adbstats(dns_adb_t *adb, isc_statscounter_t counter) {
+ if (adb->view->adbstats != NULL)
+ isc_stats_increment(adb->view->adbstats, 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);
+}
+
+/*
+ * Hashing is most efficient if the number of buckets is prime.
+ * The sequence below is the closest previous primes to 2^n and
+ * 1.5 * 2^n, for values of n from 10 to 28. (The tables will
+ * no longer grow beyond 2^28 entries.)
+ */
+static const unsigned nbuckets[] = { 1021, 1531, 2039, 3067, 4093, 6143,
+ 8191, 12281, 16381, 24571, 32749,
+ 49193, 65521, 98299, 131071, 199603,
+ 262139, 393209, 524287, 768431, 1048573,
+ 1572853, 2097143, 3145721, 4194301,
+ 6291449, 8388593, 12582893, 16777213,
+ 25165813, 33554393, 50331599, 67108859,
+ 100663291, 134217689, 201326557,
+ 268535431, 0 };
+
+static void
+grow_entries(isc_task_t *task, isc_event_t *ev) {
+ dns_adb_t *adb;
+ dns_adbentry_t *e;
+ dns_adbentrylist_t *newdeadentries = NULL;
+ dns_adbentrylist_t *newentries = NULL;
+ bool *newentry_sd = NULL;
+ isc_mutex_t *newentrylocks = NULL;
+ isc_result_t result;
+ unsigned int *newentry_refcnt = NULL;
+ unsigned int i, n, bucket;
+
+ adb = ev->ev_arg;
+ INSIST(DNS_ADB_VALID(adb));
+
+ isc_event_free(&ev);
+
+ result = isc_task_beginexclusive(task);
+ if (result != ISC_R_SUCCESS)
+ goto check_exit;
+
+ i = 0;
+ while (nbuckets[i] != 0 && adb->nentries >= nbuckets[i])
+ i++;
+ if (nbuckets[i] != 0)
+ n = nbuckets[i];
+ else
+ goto done;
+
+ DP(ISC_LOG_INFO, "adb: grow_entries to %u starting", n);
+
+ /*
+ * Are we shutting down?
+ */
+ for (i = 0; i < adb->nentries; i++)
+ if (adb->entry_sd[i])
+ goto cleanup;
+
+ /*
+ * Grab all the resources we need.
+ */
+ newentries = isc_mem_get(adb->mctx, sizeof(*newentries) * n);
+ newdeadentries = isc_mem_get(adb->mctx, sizeof(*newdeadentries) * n);
+ newentrylocks = isc_mem_get(adb->mctx, sizeof(*newentrylocks) * n);
+ newentry_sd = isc_mem_get(adb->mctx, sizeof(*newentry_sd) * n);
+ newentry_refcnt = isc_mem_get(adb->mctx, sizeof(*newentry_refcnt) * n);
+ if (newentries == NULL || newdeadentries == NULL ||
+ newentrylocks == NULL || newentry_sd == NULL ||
+ newentry_refcnt == NULL)
+ goto cleanup;
+
+ /*
+ * Initialise the new resources.
+ */
+ result = isc_mutexblock_init(newentrylocks, n);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ for (i = 0; i < n; i++) {
+ ISC_LIST_INIT(newentries[i]);
+ ISC_LIST_INIT(newdeadentries[i]);
+ newentry_sd[i] = false;
+ newentry_refcnt[i] = 0;
+ adb->irefcnt++;
+ }
+
+ /*
+ * Move entries to new arrays.
+ */
+ for (i = 0; i < adb->nentries; i++) {
+ e = ISC_LIST_HEAD(adb->entries[i]);
+ while (e != NULL) {
+ ISC_LIST_UNLINK(adb->entries[i], e, plink);
+ bucket = isc_sockaddr_hash(&e->sockaddr, true) % n;
+ e->lock_bucket = bucket;
+ ISC_LIST_APPEND(newentries[bucket], e, plink);
+ INSIST(adb->entry_refcnt[i] > 0);
+ adb->entry_refcnt[i]--;
+ newentry_refcnt[bucket]++;
+ e = ISC_LIST_HEAD(adb->entries[i]);
+ }
+ e = ISC_LIST_HEAD(adb->deadentries[i]);
+ while (e != NULL) {
+ ISC_LIST_UNLINK(adb->deadentries[i], e, plink);
+ bucket = isc_sockaddr_hash(&e->sockaddr, true) % n;
+ e->lock_bucket = bucket;
+ ISC_LIST_APPEND(newdeadentries[bucket], e, plink);
+ INSIST(adb->entry_refcnt[i] > 0);
+ adb->entry_refcnt[i]--;
+ newentry_refcnt[bucket]++;
+ e = ISC_LIST_HEAD(adb->deadentries[i]);
+ }
+ INSIST(adb->entry_refcnt[i] == 0);
+ adb->irefcnt--;
+ }
+
+ /*
+ * Cleanup old resources.
+ */
+ DESTROYMUTEXBLOCK(adb->entrylocks, adb->nentries);
+ isc_mem_put(adb->mctx, adb->entries,
+ sizeof(*adb->entries) * adb->nentries);
+ isc_mem_put(adb->mctx, adb->deadentries,
+ sizeof(*adb->deadentries) * adb->nentries);
+ isc_mem_put(adb->mctx, adb->entrylocks,
+ sizeof(*adb->entrylocks) * adb->nentries);
+ isc_mem_put(adb->mctx, adb->entry_sd,
+ sizeof(*adb->entry_sd) * adb->nentries);
+ isc_mem_put(adb->mctx, adb->entry_refcnt,
+ sizeof(*adb->entry_refcnt) * adb->nentries);
+
+ /*
+ * Install new resources.
+ */
+ adb->entries = newentries;
+ adb->deadentries = newdeadentries;
+ adb->entrylocks = newentrylocks;
+ adb->entry_sd = newentry_sd;
+ adb->entry_refcnt = newentry_refcnt;
+ adb->nentries = n;
+
+ set_adbstat(adb, adb->nentries, dns_adbstats_nentries);
+
+ /*
+ * Only on success do we set adb->growentries_sent to false.
+ * This will prevent us being continuously being called on error.
+ */
+ adb->growentries_sent = false;
+ goto done;
+
+ cleanup:
+ if (newentries != NULL)
+ isc_mem_put(adb->mctx, newentries,
+ sizeof(*newentries) * n);
+ if (newdeadentries != NULL)
+ isc_mem_put(adb->mctx, newdeadentries,
+ sizeof(*newdeadentries) * n);
+ if (newentrylocks != NULL)
+ isc_mem_put(adb->mctx, newentrylocks,
+ sizeof(*newentrylocks) * n);
+ if (newentry_sd != NULL)
+ isc_mem_put(adb->mctx, newentry_sd,
+ sizeof(*newentry_sd) * n);
+ if (newentry_refcnt != NULL)
+ isc_mem_put(adb->mctx, newentry_refcnt,
+ sizeof(*newentry_refcnt) * n);
+ done:
+ isc_task_endexclusive(task);
+
+ check_exit:
+ LOCK(&adb->lock);
+ if (dec_adb_irefcnt(adb))
+ check_exit(adb);
+ UNLOCK(&adb->lock);
+ DP(ISC_LOG_INFO, "adb: grow_entries finished");
+}
+
+static void
+grow_names(isc_task_t *task, isc_event_t *ev) {
+ dns_adb_t *adb;
+ dns_adbname_t *name;
+ dns_adbnamelist_t *newdeadnames = NULL;
+ dns_adbnamelist_t *newnames = NULL;
+ bool *newname_sd = NULL;
+ isc_mutex_t *newnamelocks = NULL;
+ isc_result_t result;
+ unsigned int *newname_refcnt = NULL;
+ unsigned int i, n, bucket;
+
+ adb = ev->ev_arg;
+ INSIST(DNS_ADB_VALID(adb));
+
+ isc_event_free(&ev);
+
+ result = isc_task_beginexclusive(task);
+ if (result != ISC_R_SUCCESS)
+ goto check_exit;
+
+ i = 0;
+ while (nbuckets[i] != 0 && adb->nnames >= nbuckets[i])
+ i++;
+ if (nbuckets[i] != 0)
+ n = nbuckets[i];
+ else
+ goto done;
+
+ DP(ISC_LOG_INFO, "adb: grow_names to %u starting", n);
+
+ /*
+ * Are we shutting down?
+ */
+ for (i = 0; i < adb->nnames; i++)
+ if (adb->name_sd[i])
+ goto cleanup;
+
+ /*
+ * Grab all the resources we need.
+ */
+ newnames = isc_mem_get(adb->mctx, sizeof(*newnames) * n);
+ newdeadnames = isc_mem_get(adb->mctx, sizeof(*newdeadnames) * n);
+ newnamelocks = isc_mem_get(adb->mctx, sizeof(*newnamelocks) * n);
+ newname_sd = isc_mem_get(adb->mctx, sizeof(*newname_sd) * n);
+ newname_refcnt = isc_mem_get(adb->mctx, sizeof(*newname_refcnt) * n);
+ if (newnames == NULL || newdeadnames == NULL ||
+ newnamelocks == NULL || newname_sd == NULL ||
+ newname_refcnt == NULL)
+ goto cleanup;
+
+ /*
+ * Initialise the new resources.
+ */
+ result = isc_mutexblock_init(newnamelocks, n);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ for (i = 0; i < n; i++) {
+ ISC_LIST_INIT(newnames[i]);
+ ISC_LIST_INIT(newdeadnames[i]);
+ newname_sd[i] = false;
+ newname_refcnt[i] = 0;
+ adb->irefcnt++;
+ }
+
+ /*
+ * Move names to new arrays.
+ */
+ for (i = 0; i < adb->nnames; i++) {
+ name = ISC_LIST_HEAD(adb->names[i]);
+ while (name != NULL) {
+ ISC_LIST_UNLINK(adb->names[i], name, plink);
+ bucket = dns_name_fullhash(&name->name, true) % n;
+ name->lock_bucket = bucket;
+ ISC_LIST_APPEND(newnames[bucket], name, plink);
+ INSIST(adb->name_refcnt[i] > 0);
+ adb->name_refcnt[i]--;
+ newname_refcnt[bucket]++;
+ name = ISC_LIST_HEAD(adb->names[i]);
+ }
+ name = ISC_LIST_HEAD(adb->deadnames[i]);
+ while (name != NULL) {
+ ISC_LIST_UNLINK(adb->deadnames[i], name, plink);
+ bucket = dns_name_fullhash(&name->name, true) % n;
+ name->lock_bucket = bucket;
+ ISC_LIST_APPEND(newdeadnames[bucket], name, plink);
+ INSIST(adb->name_refcnt[i] > 0);
+ adb->name_refcnt[i]--;
+ newname_refcnt[bucket]++;
+ name = ISC_LIST_HEAD(adb->deadnames[i]);
+ }
+ INSIST(adb->name_refcnt[i] == 0);
+ adb->irefcnt--;
+ }
+
+ /*
+ * Cleanup old resources.
+ */
+ DESTROYMUTEXBLOCK(adb->namelocks, adb->nnames);
+ isc_mem_put(adb->mctx, adb->names,
+ sizeof(*adb->names) * adb->nnames);
+ isc_mem_put(adb->mctx, adb->deadnames,
+ sizeof(*adb->deadnames) * adb->nnames);
+ isc_mem_put(adb->mctx, adb->namelocks,
+ sizeof(*adb->namelocks) * adb->nnames);
+ isc_mem_put(adb->mctx, adb->name_sd,
+ sizeof(*adb->name_sd) * adb->nnames);
+ isc_mem_put(adb->mctx, adb->name_refcnt,
+ sizeof(*adb->name_refcnt) * adb->nnames);
+
+ /*
+ * Install new resources.
+ */
+ adb->names = newnames;
+ adb->deadnames = newdeadnames;
+ adb->namelocks = newnamelocks;
+ adb->name_sd = newname_sd;
+ adb->name_refcnt = newname_refcnt;
+ adb->nnames = n;
+
+ set_adbstat(adb, adb->nnames, dns_adbstats_nnames);
+
+ /*
+ * Only on success do we set adb->grownames_sent to false.
+ * This will prevent us being continuously being called on error.
+ */
+ adb->grownames_sent = false;
+ goto done;
+
+ cleanup:
+ if (newnames != NULL)
+ isc_mem_put(adb->mctx, newnames, sizeof(*newnames) * n);
+ if (newdeadnames != NULL)
+ isc_mem_put(adb->mctx, newdeadnames, sizeof(*newdeadnames) * n);
+ if (newnamelocks != NULL)
+ isc_mem_put(adb->mctx, newnamelocks, sizeof(*newnamelocks) * n);
+ if (newname_sd != NULL)
+ isc_mem_put(adb->mctx, newname_sd, sizeof(*newname_sd) * n);
+ if (newname_refcnt != NULL)
+ isc_mem_put(adb->mctx, newname_refcnt,
+ sizeof(*newname_refcnt) * n);
+ done:
+ isc_task_endexclusive(task);
+
+ check_exit:
+ LOCK(&adb->lock);
+ if (dec_adb_irefcnt(adb))
+ check_exit(adb);
+ UNLOCK(&adb->lock);
+ DP(ISC_LOG_INFO, "adb: grow_names finished");
+}
+
+/*
+ * 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;
+ bool 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 = 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);
+ memmove(&ina.s_addr, rdata.data, 4);
+ isc_sockaddr_fromin(&sockaddr, &ina, 0);
+ hookhead = &adbname->v4;
+ } else {
+ INSIST(rdata.length == 16);
+ memmove(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,
+ now);
+ 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;
+ entry->nh = 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++;
+ foundentry->nh++;
+ nh->entry = foundentry;
+ } else
+ free_adbnamehook(adb, &nh);
+ }
+
+ new_addresses_added = 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 if (rdataset->trust == dns_trust_ultimate)
+ rdataset->ttl = 0;
+ 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,
+ ISC_MIN(now + ADB_ENTRY_WINDOW,
+ 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,
+ ISC_MIN(now + ADB_ENTRY_WINDOW,
+ 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 bool
+kill_name(dns_adbname_t **n, isc_eventtype_t ev) {
+ dns_adbname_t *name;
+ bool result = false;
+ bool result4, result6;
+ int bucket;
+ 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);
+ result6 = clean_namehooks(adb, &name->v6);
+ clean_target(adb, &name->target);
+ result = (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 == false);
+ result = unlink_name(adb, name);
+ free_adbname(adb, &name);
+ if (result)
+ result = dec_adb_irefcnt(adb);
+ } else {
+ cancel_fetches_at_name(name);
+ if (!NAME_DEAD(name)) {
+ bucket = name->lock_bucket;
+ ISC_LIST_UNLINK(adb->names[bucket], name, plink);
+ ISC_LIST_APPEND(adb->deadnames[bucket], name, plink);
+ name->flags |= NAME_IS_DEAD;
+ }
+ }
+ return (result);
+}
+
+/*
+ * Requires the name's bucket be locked and no entry buckets be locked.
+ */
+static bool
+check_expire_namehooks(dns_adbname_t *name, isc_stdtime_t now) {
+ dns_adb_t *adb;
+ bool result4 = false;
+ bool result6 = 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);
+ 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);
+ 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 (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 bool
+unlink_name(dns_adb_t *adb, dns_adbname_t *name) {
+ int bucket;
+ bool result = false;
+
+ bucket = name->lock_bucket;
+ INSIST(bucket != DNS_ADB_INVALIDBUCKET);
+
+ if (NAME_DEAD(name))
+ ISC_LIST_UNLINK(adb->deadnames[bucket], name, plink);
+ else
+ 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 = 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) {
+ int i;
+ dns_adbentry_t *e;
+
+ if (isc_mem_isovermem(adb->mctx)) {
+ for (i = 0; i < 2; i++) {
+ e = ISC_LIST_TAIL(adb->entries[bucket]);
+ if (e == NULL)
+ break;
+ if (e->refcnt == 0) {
+ unlink_entry(adb, e);
+ free_adbentry(adb, &e);
+ continue;
+ }
+ INSIST((e->flags & ENTRY_IS_DEAD) == 0);
+ e->flags |= ENTRY_IS_DEAD;
+ ISC_LIST_UNLINK(adb->entries[bucket], e, plink);
+ ISC_LIST_PREPEND(adb->deadentries[bucket], e, plink);
+ }
+ }
+
+ 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 bool
+unlink_entry(dns_adb_t *adb, dns_adbentry_t *entry) {
+ int bucket;
+ bool result = false;
+
+ bucket = entry->lock_bucket;
+ INSIST(bucket != DNS_ADB_INVALIDBUCKET);
+
+ if ((entry->flags & ENTRY_IS_DEAD) != 0)
+ ISC_LIST_UNLINK(adb->deadentries[bucket], entry, plink);
+ else
+ 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 = 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 bool
+shutdown_names(dns_adb_t *adb) {
+ unsigned int bucket;
+ bool result = false;
+ dns_adbname_t *name;
+ dns_adbname_t *next_name;
+
+ for (bucket = 0; bucket < adb->nnames; bucket++) {
+ LOCK(&adb->namelocks[bucket]);
+ adb->name_sd[bucket] = 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 == 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 == false);
+ result = kill_name(&name,
+ DNS_EVENT_ADBSHUTDOWN);
+ 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 bool
+shutdown_entries(dns_adb_t *adb) {
+ unsigned int bucket;
+ bool result = false;
+ dns_adbentry_t *entry;
+ dns_adbentry_t *next_entry;
+
+ for (bucket = 0; bucket < adb->nentries; bucket++) {
+ LOCK(&adb->entrylocks[bucket]);
+ adb->entry_sd[bucket] = true;
+
+ entry = ISC_LIST_HEAD(adb->entries[bucket]);
+ if (adb->entry_refcnt[bucket] == 0) {
+ /*
+ * 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 bool
+clean_namehooks(dns_adb_t *adb, dns_adbnamehooklist_t *namehooks) {
+ dns_adbentry_t *entry;
+ dns_adbnamehook_t *namehook;
+ int addr_bucket;
+ bool result = false;
+ bool overmem = isc_mem_isovermem(adb->mctx);
+
+ 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;
+ INSIST(addr_bucket != DNS_ADB_INVALIDBUCKET);
+ LOCK(&adb->entrylocks[addr_bucket]);
+ }
+
+ entry->nh--;
+ result = dec_entry_refcnt(adb, overmem, entry,
+ 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.
+ */
+ prefix = dns_fixedname_initname(&fixed1);
+ new_target = dns_fixedname_initname(&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;
+ bool 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 = 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 = 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 = true;
+ break;
+ default:
+ find->flags &= ~addrs;
+ process = 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);
+ find->flags |= FIND_EVENT_SENT;
+ } 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_out); /* Sanity check. */
+ ISC_EVENT_INIT(&adb->cevent, sizeof(adb->cevent), 0, NULL,
+ DNS_EVENT_ADBCONTROL, shutdown_task, adb,
+ adb, NULL, NULL);
+ event = &adb->cevent;
+ isc_task_send(adb->task, &event);
+ adb->cevent_out = true;
+ }
+}
+
+static inline bool
+dec_adb_irefcnt(dns_adb_t *adb) {
+ isc_event_t *event;
+ isc_task_t *etask;
+ bool result = 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 = 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, bool lock) {
+ int bucket;
+
+ bucket = entry->lock_bucket;
+
+ if (lock)
+ LOCK(&adb->entrylocks[bucket]);
+
+ entry->refcnt++;
+
+ if (lock)
+ UNLOCK(&adb->entrylocks[bucket]);
+}
+
+static inline bool
+dec_entry_refcnt(dns_adb_t *adb, bool overmem, dns_adbentry_t *entry,
+ bool lock)
+{
+ int bucket;
+ bool destroy_entry;
+ bool result = false;
+
+ bucket = entry->lock_bucket;
+
+ if (lock)
+ LOCK(&adb->entrylocks[bucket]);
+
+ INSIST(entry->refcnt > 0);
+ entry->refcnt--;
+
+ destroy_entry = false;
+ if (entry->refcnt == 0 &&
+ (adb->entry_sd[bucket] || entry->expires == 0 || overmem ||
+ (entry->flags & ENTRY_IS_DEAD) != 0)) {
+ destroy_entry = 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);
+
+ LOCK(&adb->namescntlock);
+ adb->namescnt++;
+ inc_adbstats(adb, dns_adbstats_namescnt);
+ if (!adb->grownames_sent && adb->excl != NULL &&
+ adb->namescnt > (adb->nnames * 8))
+ {
+ isc_event_t *event = &adb->grownames;
+ inc_adb_irefcnt(adb);
+ isc_task_send(adb->excl, &event);
+ adb->grownames_sent = true;
+ }
+ UNLOCK(&adb->namescntlock);
+
+ 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);
+ LOCK(&adb->namescntlock);
+ adb->namescnt--;
+ dec_adbstats(adb, dns_adbstats_namescnt);
+ UNLOCK(&adb->namescntlock);
+}
+
+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;
+ 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->nh = 0;
+ e->flags = 0;
+ e->udpsize = 0;
+ e->edns = 0;
+ e->completed = 0;
+ e->timeouts = 0;
+ e->plain = 0;
+ e->plainto = 0;
+ e->to4096 = 0;
+ e->to1432 = 0;
+ e->to1232 = 0;
+ e->to512 = 0;
+ e->cookie = NULL;
+ e->cookielen = 0;
+ isc_random_get(&r);
+ e->srtt = (r & 0x1f) + 1;
+ e->lastage = 0;
+ e->expires = 0;
+ e->active = 0;
+ e->mode = 0;
+ e->quota = adb->quota;
+ e->atr = 0.0;
+ ISC_LIST_INIT(e->lameinfo);
+ ISC_LINK_INIT(e, plink);
+ LOCK(&adb->entriescntlock);
+ adb->entriescnt++;
+ inc_adbstats(adb, dns_adbstats_entriescnt);
+ if (!adb->growentries_sent && adb->excl != NULL &&
+ adb->entriescnt > (adb->nentries * 8))
+ {
+ isc_event_t *event = &adb->growentries;
+ inc_adb_irefcnt(adb);
+ isc_task_send(adb->excl, &event);
+ adb->growentries_sent = true;
+ }
+ UNLOCK(&adb->entriescntlock);
+
+ 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;
+
+ if (e->cookie != NULL)
+ isc_mem_put(adb->mctx, e->cookie, e->cookielen);
+
+ 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);
+ LOCK(&adb->entriescntlock);
+ adb->entriescnt--;
+ dec_adbstats(adb, dns_adbstats_entriescnt);
+ UNLOCK(&adb->entriescntlock);
+}
+
+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->fetch = NULL;
+
+ dns_rdataset_init(&f->rdataset);
+
+ f->magic = DNS_ADBFETCH_MAGIC;
+
+ return (f);
+}
+
+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 (dns_rdataset_isassociated(&f->rdataset))
+ dns_rdataset_disassociate(&f->rdataset);
+
+ isc_mempool_put(adb->afmp, f);
+}
+
+static inline bool
+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;
+ ai->dscp = -1;
+ 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, false) % adb->nnames;
+
+ 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,
+ isc_stdtime_t now)
+{
+ dns_adbentry_t *entry, *entry_next;
+ int bucket;
+
+ bucket = isc_sockaddr_hash(addr, true) % adb->nentries;
+
+ 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;
+ }
+
+ /* Search the list, while cleaning up expired entries. */
+ for (entry = ISC_LIST_HEAD(adb->entries[bucket]);
+ entry != NULL;
+ entry = entry_next) {
+ entry_next = ISC_LIST_NEXT(entry, plink);
+ (void)check_expire_entry(adb, &entry, now);
+ if (entry != NULL &&
+ (entry->expires == 0 || entry->expires > now) &&
+ isc_sockaddr_equal(addr, &entry->sockaddr)) {
+ ISC_LIST_UNLINK(adb->entries[bucket], entry, plink);
+ ISC_LIST_PREPEND(adb->entries[bucket], entry, plink);
+ return (entry);
+ }
+ }
+
+ return (NULL);
+}
+
+/*
+ * Entry bucket MUST be locked!
+ */
+static bool
+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;
+ bool is_bad;
+
+ is_bad = false;
+
+ li = ISC_LIST_HEAD(entry->lameinfo);
+ if (li == NULL)
+ return (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 = true;
+
+ li = next_li;
+ }
+
+ return (is_bad);
+}
+
+static void
+log_quota(dns_adbentry_t *entry, const char *fmt, ...) {
+ va_list ap;
+ char msgbuf[2048];
+ char addrbuf[ISC_NETADDR_FORMATSIZE];
+ isc_netaddr_t netaddr;
+
+ va_start(ap, fmt);
+ vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
+ va_end(ap);
+
+ isc_netaddr_fromsockaddr(&netaddr, &entry->sockaddr);
+ isc_netaddr_format(&netaddr, addrbuf, sizeof(addrbuf));
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ADB,
+ ISC_LOG_INFO, "adb: quota %s (%u/%u): %s",
+ addrbuf, entry->active, entry->quota, msgbuf);
+}
+
+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;
+ INSIST(bucket != DNS_ADB_INVALIDBUCKET);
+ LOCK(&adb->entrylocks[bucket]);
+
+ if (entry->quota != 0 &&
+ entry->active >= entry->quota)
+ {
+ find->options |=
+ (DNS_ADBFIND_LAMEPRUNED|
+ DNS_ADBFIND_OVERQUOTA);
+ goto nextv4;
+ }
+
+ 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, 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;
+ INSIST(bucket != DNS_ADB_INVALIDBUCKET);
+ LOCK(&adb->entrylocks[bucket]);
+
+ if (entry->quota != 0 &&
+ entry->active >= entry->quota)
+ {
+ find->options |=
+ (DNS_ADBFIND_LAMEPRUNED|
+ DNS_ADBFIND_OVERQUOTA);
+ goto nextv6;
+ }
+
+ 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, 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 bool
+check_expire_name(dns_adbname_t **namep, isc_stdtime_t now) {
+ dns_adbname_t *name;
+ bool result = 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);
+ *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;
+ dns_adbname_t *victim, *next_victim;
+ bool overmem = isc_mem_isovermem(adb->mctx);
+ 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) {
+ INSIST(!NAME_DEAD(victim));
+ scans++;
+ next_victim = ISC_LIST_PREV(victim, plink);
+ (void)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) ==
+ false);
+ victims++;
+ }
+
+ next:
+ if (!overmem)
+ break;
+ }
+}
+
+/*
+ * Entry bucket must be locked; adb may be locked; no other locks held.
+ */
+static bool
+check_expire_entry(dns_adb_t *adb, dns_adbentry_t **entryp, isc_stdtime_t now)
+{
+ dns_adbentry_t *entry;
+ bool result = 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 bool
+cleanup_names(dns_adb_t *adb, int bucket, isc_stdtime_t now) {
+ dns_adbname_t *name;
+ dns_adbname_t *next_name;
+ bool result = 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 == 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 bool
+cleanup_entries(dns_adb_t *adb, int bucket, isc_stdtime_t now) {
+ dns_adbentry_t *entry, *next_entry;
+ bool result = 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 == 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);
+ if (adb->excl != NULL)
+ isc_task_detach(&adb->excl);
+
+ 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, adb->nentries);
+ isc_mem_put(adb->mctx, adb->entries,
+ sizeof(*adb->entries) * adb->nentries);
+ isc_mem_put(adb->mctx, adb->deadentries,
+ sizeof(*adb->deadentries) * adb->nentries);
+ isc_mem_put(adb->mctx, adb->entrylocks,
+ sizeof(*adb->entrylocks) * adb->nentries);
+ isc_mem_put(adb->mctx, adb->entry_sd,
+ sizeof(*adb->entry_sd) * adb->nentries);
+ isc_mem_put(adb->mctx, adb->entry_refcnt,
+ sizeof(*adb->entry_refcnt) * adb->nentries);
+
+ DESTROYMUTEXBLOCK(adb->namelocks, adb->nnames);
+ isc_mem_put(adb->mctx, adb->names,
+ sizeof(*adb->names) * adb->nnames);
+ isc_mem_put(adb->mctx, adb->deadnames,
+ sizeof(*adb->deadnames) * adb->nnames);
+ isc_mem_put(adb->mctx, adb->namelocks,
+ sizeof(*adb->namelocks) * adb->nnames);
+ isc_mem_put(adb->mctx, adb->name_sd,
+ sizeof(*adb->name_sd) * adb->nnames);
+ isc_mem_put(adb->mctx, adb->name_refcnt,
+ sizeof(*adb->name_refcnt) * adb->nnames);
+
+ DESTROYLOCK(&adb->reflock);
+ DESTROYLOCK(&adb->lock);
+ DESTROYLOCK(&adb->mplock);
+ DESTROYLOCK(&adb->overmemlock);
+ DESTROYLOCK(&adb->entriescntlock);
+ DESTROYLOCK(&adb->namescntlock);
+
+ 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;
+ unsigned 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->excl = NULL;
+ adb->mctx = NULL;
+ adb->view = view;
+ adb->taskmgr = taskmgr;
+ adb->next_cleanbucket = 0;
+ ISC_EVENT_INIT(&adb->cevent, sizeof(adb->cevent),
+ 0, NULL, 0, NULL, NULL, NULL, NULL, NULL);
+ adb->cevent_out = false;
+ adb->shutting_down = false;
+ ISC_LIST_INIT(adb->whenshutdown);
+
+ adb->nentries = nbuckets[0];
+ adb->entriescnt = 0;
+ adb->entries = NULL;
+ adb->deadentries = NULL;
+ adb->entry_sd = NULL;
+ adb->entry_refcnt = NULL;
+ adb->entrylocks = NULL;
+ ISC_EVENT_INIT(&adb->growentries, sizeof(adb->growentries), 0, NULL,
+ DNS_EVENT_ADBGROWENTRIES, grow_entries, adb,
+ adb, NULL, NULL);
+ adb->growentries_sent = false;
+
+ adb->quota = 0;
+ adb->atr_freq = 0;
+ adb->atr_low = 0.0;
+ adb->atr_high = 0.0;
+ adb->atr_discount = 0.0;
+
+ adb->nnames = nbuckets[0];
+ adb->namescnt = 0;
+ adb->names = NULL;
+ adb->deadnames = NULL;
+ adb->name_sd = NULL;
+ adb->name_refcnt = NULL;
+ adb->namelocks = NULL;
+ ISC_EVENT_INIT(&adb->grownames, sizeof(adb->grownames), 0, NULL,
+ DNS_EVENT_ADBGROWNAMES, grow_names, adb,
+ adb, NULL, NULL);
+ adb->grownames_sent = false;
+
+ result = isc_taskmgr_excltask(adb->taskmgr, &adb->excl);
+ if (result != ISC_R_SUCCESS) {
+ DP(DEF_LEVEL, "adb: task-exclusive mode unavailable, "
+ "intializing table sizes to %u\n",
+ nbuckets[11]);
+ adb->nentries = nbuckets[11];
+ adb->nnames = nbuckets[11];
+ }
+
+ 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;
+
+ result = isc_mutex_init(&adb->entriescntlock);
+ if (result != ISC_R_SUCCESS)
+ goto fail0f;
+
+ result = isc_mutex_init(&adb->namescntlock);
+ if (result != ISC_R_SUCCESS)
+ goto fail0g;
+
+#define ALLOCENTRY(adb, el) \
+ do { \
+ (adb)->el = isc_mem_get((adb)->mctx, \
+ sizeof(*(adb)->el) * (adb)->nentries); \
+ if ((adb)->el == NULL) { \
+ result = ISC_R_NOMEMORY; \
+ goto fail1; \
+ }\
+ } while (0)
+ ALLOCENTRY(adb, entries);
+ ALLOCENTRY(adb, deadentries);
+ ALLOCENTRY(adb, entrylocks);
+ ALLOCENTRY(adb, entry_sd);
+ ALLOCENTRY(adb, entry_refcnt);
+#undef ALLOCENTRY
+
+#define ALLOCNAME(adb, el) \
+ do { \
+ (adb)->el = isc_mem_get((adb)->mctx, \
+ sizeof(*(adb)->el) * (adb)->nnames); \
+ if ((adb)->el == NULL) { \
+ result = ISC_R_NOMEMORY; \
+ goto fail1; \
+ }\
+ } while (0)
+ ALLOCNAME(adb, names);
+ ALLOCNAME(adb, deadnames);
+ ALLOCNAME(adb, namelocks);
+ ALLOCNAME(adb, name_sd);
+ ALLOCNAME(adb, name_refcnt);
+#undef ALLOCNAME
+
+ /*
+ * Initialize the bucket locks for names and elements.
+ * May as well initialize the list heads, too.
+ */
+ result = isc_mutexblock_init(adb->namelocks, adb->nnames);
+ if (result != ISC_R_SUCCESS)
+ goto fail1;
+ for (i = 0; i < adb->nnames; i++) {
+ ISC_LIST_INIT(adb->names[i]);
+ ISC_LIST_INIT(adb->deadnames[i]);
+ adb->name_sd[i] = false;
+ adb->name_refcnt[i] = 0;
+ adb->irefcnt++;
+ }
+ for (i = 0; i < adb->nentries; i++) {
+ ISC_LIST_INIT(adb->entries[i]);
+ ISC_LIST_INIT(adb->deadentries[i]);
+ adb->entry_sd[i] = false;
+ adb->entry_refcnt[i] = 0;
+ adb->irefcnt++;
+ }
+ result = isc_mutexblock_init(adb->entrylocks, adb->nentries);
+ 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);
+
+ result = isc_stats_create(adb->mctx, &view->adbstats, dns_adbstats_max);
+ if (result != ISC_R_SUCCESS)
+ goto fail3;
+
+ set_adbstat(adb, adb->nentries, dns_adbstats_nentries);
+ set_adbstat(adb, adb->nnames, dns_adbstats_nnames);
+
+ /*
+ * 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, adb->nentries);
+
+ fail2: /* clean up namelocks */
+ DESTROYMUTEXBLOCK(adb->namelocks, adb->nnames);
+
+ fail1: /* clean up only allocated memory */
+ if (adb->entries != NULL)
+ isc_mem_put(adb->mctx, adb->entries,
+ sizeof(*adb->entries) * adb->nentries);
+ if (adb->deadentries != NULL)
+ isc_mem_put(adb->mctx, adb->deadentries,
+ sizeof(*adb->deadentries) * adb->nentries);
+ if (adb->entrylocks != NULL)
+ isc_mem_put(adb->mctx, adb->entrylocks,
+ sizeof(*adb->entrylocks) * adb->nentries);
+ if (adb->entry_sd != NULL)
+ isc_mem_put(adb->mctx, adb->entry_sd,
+ sizeof(*adb->entry_sd) * adb->nentries);
+ if (adb->entry_refcnt != NULL)
+ isc_mem_put(adb->mctx, adb->entry_refcnt,
+ sizeof(*adb->entry_refcnt) * adb->nentries);
+ if (adb->names != NULL)
+ isc_mem_put(adb->mctx, adb->names,
+ sizeof(*adb->names) * adb->nnames);
+ if (adb->deadnames != NULL)
+ isc_mem_put(adb->mctx, adb->deadnames,
+ sizeof(*adb->deadnames) * adb->nnames);
+ if (adb->namelocks != NULL)
+ isc_mem_put(adb->mctx, adb->namelocks,
+ sizeof(*adb->namelocks) * adb->nnames);
+ if (adb->name_sd != NULL)
+ isc_mem_put(adb->mctx, adb->name_sd,
+ sizeof(*adb->name_sd) * adb->nnames);
+ if (adb->name_refcnt != NULL)
+ isc_mem_put(adb->mctx, adb->name_refcnt,
+ sizeof(*adb->name_refcnt) * adb->nnames);
+ 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->namescntlock);
+ fail0g:
+ DESTROYLOCK(&adb->entriescntlock);
+ fail0f:
+ DESTROYLOCK(&adb->overmemlock);
+ fail0e:
+ DESTROYLOCK(&adb->reflock);
+ fail0d:
+ DESTROYLOCK(&adb->mplock);
+ fail0c:
+ DESTROYLOCK(&adb->lock);
+ fail0b:
+ if (adb->excl != NULL)
+ isc_task_detach(&adb->excl);
+ 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;
+ bool 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 = (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 *tclone;
+ isc_event_t *event;
+ bool zeroirefcnt;
+
+ /*
+ * 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 = (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 {
+ tclone = NULL;
+ isc_task_attach(task, &tclone);
+ event->ev_sender = tclone;
+ ISC_LIST_APPEND(adb->whenshutdown, event, ev_link);
+ }
+
+ UNLOCK(&adb->reflock);
+ UNLOCK(&adb->lock);
+}
+
+static void
+shutdown_stage2(isc_task_t *task, isc_event_t *event) {
+ dns_adb_t *adb;
+
+ UNUSED(task);
+
+ adb = event->ev_arg;
+ INSIST(DNS_ADB_VALID(adb));
+
+ LOCK(&adb->lock);
+ INSIST(adb->shutting_down);
+ adb->cevent_out = false;
+ (void)shutdown_names(adb);
+ (void)shutdown_entries(adb);
+ if (dec_adb_irefcnt(adb))
+ check_exit(adb);
+ UNLOCK(&adb->lock);
+}
+
+void
+dns_adb_shutdown(dns_adb_t *adb) {
+ isc_event_t *event;
+
+ /*
+ * Shutdown 'adb'.
+ */
+
+ LOCK(&adb->lock);
+
+ if (!adb->shutting_down) {
+ adb->shutting_down = true;
+ isc_mem_setwater(adb->mctx, water, adb, 0, 0);
+ /*
+ * Isolate shutdown_names and shutdown_entries calls.
+ */
+ inc_adb_irefcnt(adb);
+ ISC_EVENT_INIT(&adb->cevent, sizeof(adb->cevent), 0, NULL,
+ DNS_EVENT_ADBCONTROL, shutdown_stage2, adb,
+ adb, NULL, NULL);
+ adb->cevent_out = true;
+ event = &adb->cevent;
+ isc_task_send(adb->task, &event);
+ }
+
+ 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)
+{
+ return (dns_adb_createfind2(adb, task, action, arg, name,
+ qname, qtype, options, now,
+ target, port, 0, NULL, findp));
+}
+
+isc_result_t
+dns_adb_createfind2(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, unsigned int depth, isc_counter_t *qc,
+ dns_adbfind_t **findp)
+{
+ dns_adbfind_t *find;
+ dns_adbname_t *adbname;
+ int bucket;
+ bool want_event, start_at_zone, alias, have_address;
+ isc_result_t result;
+ unsigned int wanted_addresses;
+ unsigned int wanted_fetches;
+ unsigned int query_pending;
+ char namebuf[DNS_NAME_FORMATSIZE];
+
+ 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;
+ POST(result);
+ wanted_addresses = (options & DNS_ADBFIND_ADDRESSMASK);
+ wanted_fetches = 0;
+ query_pending = 0;
+ want_event = false;
+ start_at_zone = false;
+ alias = 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);
+ }
+
+ if (isc_log_wouldlog(dns_lctx, DEF_LEVEL))
+ dns_name_format(name, namebuf, sizeof(namebuf));
+ else
+ namebuf[0] = 0;
+
+ /*
+ * 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);
+ INSIST(bucket != DNS_ADB_INVALIDBUCKET);
+ if (adb->name_sd[bucket]) {
+ DP(DEF_LEVEL,
+ "dns_adb_createfind: returning ISC_R_SHUTTINGDOWN");
+ RUNTIME_CHECK(free_adbfind(adb, &find) == 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) == 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) == 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 %s (%p) is an alias (cached)",
+ namebuf, adbname);
+ alias = 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 %s (%p) in db",
+ namebuf, adbname);
+ goto v6;
+ }
+
+ /*
+ * Did we get a CNAME or DNAME?
+ */
+ if (result == DNS_R_ALIAS) {
+ DP(DEF_LEVEL,
+ "dns_adb_createfind: name %s (%p) is an alias",
+ namebuf, adbname);
+ alias = 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 %s (%p)",
+ namebuf, adbname);
+ goto fetch;
+ }
+
+ /*
+ * Did we get a CNAME or DNAME?
+ */
+ if (result == DNS_R_ALIAS) {
+ DP(DEF_LEVEL,
+ "dns_adb_createfind: name %s (%p) is an alias",
+ namebuf, adbname);
+ alias = 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 = true;
+ else
+ have_address = 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 = true;
+
+ /*
+ * Start V4.
+ */
+ if (WANT_INET(wanted_fetches) &&
+ fetch_name(adbname, start_at_zone, depth, qc,
+ dns_rdatatype_a) == ISC_R_SUCCESS) {
+ DP(DEF_LEVEL, "dns_adb_createfind: "
+ "started A fetch for name %s (%p)",
+ namebuf, adbname);
+ }
+
+ /*
+ * Start V6.
+ */
+ if (WANT_INET6(wanted_fetches) &&
+ fetch_name(adbname, start_at_zone, depth, qc,
+ dns_rdatatype_aaaa) == ISC_R_SUCCESS) {
+ DP(DEF_LEVEL, "dns_adb_createfind: "
+ "started AAAA fetch for name %s (%p)",
+ namebuf, 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 = true;
+ if (!FIND_WANTEVENT(find))
+ want_event = false;
+ if (FIND_WANTEMPTYEVENT(find) && FIND_HAS_ADDRS(find))
+ want_event = false;
+ if ((wanted_addresses & query_pending) == 0)
+ want_event = false;
+ if (alias)
+ want_event = 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;
+ bool overmem;
+
+ 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.
+ */
+ overmem = isc_mem_isovermem(adb->mctx);
+ 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, overmem, entry, true) ==
+ 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;
+ POST(bucket);
+
+ 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) {
+ unsigned 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 < adb->nnames; i++)
+ RUNTIME_CHECK(cleanup_names(adb, i, now) == false);
+ for (i = 0; i < adb->nentries; i++)
+ RUNTIME_CHECK(cleanup_entries(adb, i, now) == false);
+
+ dump_adb(adb, f, 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, (int)(value - now));
+}
+
+static void
+dump_adb(dns_adb_t *adb, FILE *f, bool debug, isc_stdtime_t now) {
+ unsigned int i;
+ dns_adbname_t *name;
+ dns_adbentry_t *entry;
+
+ fprintf(f, ";\n; Address database dump\n;\n");
+ fprintf(f, "; [edns success/4096 timeout/1432 timeout/1232 timeout/"
+ "512 timeout]\n");
+ fprintf(f, "; [plain success/timeout]\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 < adb->nnames; i++)
+ LOCK(&adb->namelocks[i]);
+ for (i = 0; i < adb->nentries; i++)
+ LOCK(&adb->entrylocks[i]);
+
+ /*
+ * Dump the names
+ */
+ for (i = 0; i < adb->nnames; i++) {
+ name = ISC_LIST_HEAD(adb->names[i]);
+ if (name == NULL)
+ continue;
+ if (debug)
+ fprintf(f, "; bucket %u\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", adb,
+ &name->v4, debug, now);
+ print_namehook_list(f, "v6", adb,
+ &name->v6, debug, now);
+
+ if (debug) {
+ print_fetch_list(f, name);
+ print_find_list(f, name);
+ }
+ }
+ }
+
+ fprintf(f, ";\n; Unassociated entries\n;\n");
+
+ for (i = 0; i < adb->nentries; i++) {
+ entry = ISC_LIST_HEAD(adb->entries[i]);
+ while (entry != NULL) {
+ if (entry->nh == 0)
+ dump_entry(f, adb, entry, debug, now);
+ entry = ISC_LIST_NEXT(entry, plink);
+ }
+ }
+
+ /*
+ * Unlock everything
+ */
+ for (i = 0; i < adb->nentries; i++)
+ UNLOCK(&adb->entrylocks[i]);
+ for (i = 0; i < adb->nnames; i++)
+ UNLOCK(&adb->namelocks[i]);
+}
+
+static void
+dump_entry(FILE *f, dns_adb_t *adb, dns_adbentry_t *entry,
+ bool 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] [edns %u/%u/%u/%u/%u] "
+ "[plain %u/%u]", addrbuf, entry->srtt, entry->flags,
+ entry->edns, entry->to4096, entry->to1432, entry->to1232,
+ entry->to512, entry->plain, entry->plainto);
+ if (entry->udpsize != 0U)
+ fprintf(f, " [udpsize %u]", entry->udpsize);
+ if (entry->cookie != NULL) {
+ unsigned int i;
+ fprintf(f, " [cookie=");
+ for (i = 0; i < entry->cookielen; i++)
+ fprintf(f, "%02x", entry->cookie[i]);
+ fprintf(f, "]");
+ }
+ if (entry->expires != 0)
+ fprintf(f, " [ttl %d]", (int)(entry->expires - now));
+
+ if (adb != NULL && adb->quota != 0 && adb->atr_freq != 0) {
+ fprintf(f, " [atr %0.2f] [quota %u]",
+ entry->atr, entry->quota);
+ }
+
+ 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,
+ (int)(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_adb_t *adb, dns_adbnamehooklist_t *list,
+ bool 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, adb, nh->entry, debug, now);
+ }
+}
+
+static inline void
+print_fetch(FILE *f, dns_adbfetch_t *ft, const char *type) {
+ fprintf(f, "\t\tFetch(%s): %p -> { fetch %p }\n",
+ type, ft, 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);
+
+ fname = dns_fixedname_initname(&foundname);
+ dns_rdataset_init(&rdataset);
+
+ if (rdtype == dns_rdatatype_a)
+ adbname->fetch_err = FIND_ERR_UNEXPECTED;
+ else
+ adbname->fetch6_err = FIND_ERR_UNEXPECTED;
+
+ /*
+ * We need to specify whether to search static-stub zones (if
+ * configured) depending on whether this is a "start at zone" lookup,
+ * i.e., whether it's a "bailiwick" glue. If it's bailiwick (in which
+ * case NAME_STARTATZONE is set) we need to stop the search at any
+ * matching static-stub zone without looking into the cache to honor
+ * the configuration on which server we should send queries to.
+ */
+ result = dns_view_find2(adb->view, &adbname->name, rdtype, now,
+ NAME_GLUEOK(adbname) ? DNS_DBFIND_GLUEOK : 0,
+ NAME_HINTOK(adbname),
+ (adbname->flags & NAME_STARTATZONE) != 0 ?
+ true : false,
+ 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;
+ bool want_check_exit = 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);
+
+ 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));
+ /*
+ * Don't record a failure unless this is the initial
+ * fetch of a chain.
+ */
+ if (fetch->depth > 1)
+ goto out;
+ /* XXXMLG Don't pound on bad servers. */
+ if (address_type == DNS_ADBFIND_INET) {
+ name->expire_v4 = ISC_MIN(name->expire_v4, now + 10);
+ name->fetch_err = FIND_ERR_FAILURE;
+ inc_stats(adb, dns_resstatscounter_gluefetchv4fail);
+ } else {
+ name->expire_v6 = ISC_MIN(name->expire_v6, now + 10);
+ 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, bool start_at_zone,
+ unsigned int depth, isc_counter_t *qc, 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);
+ name = dns_fixedname_initname(&fixed);
+ result = dns_view_findzonecut2(adb->view, &adbname->name, name,
+ 0, 0, true, 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;
+ }
+ fetch->depth = depth;
+
+ result = dns_resolver_createfetch3(adb->view->resolver, &adbname->name,
+ type, name, nameservers, NULL,
+ NULL, 0, options, depth, qc,
+ 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;
+ isc_stdtime_t now = 0;
+
+ REQUIRE(DNS_ADB_VALID(adb));
+ REQUIRE(DNS_ADBADDRINFO_VALID(addr));
+ REQUIRE(factor <= 10);
+
+ bucket = addr->entry->lock_bucket;
+ LOCK(&adb->entrylocks[bucket]);
+
+ if (addr->entry->expires == 0 || factor == DNS_ADB_RTTADJAGE)
+ isc_stdtime_get(&now);
+ adjustsrtt(addr, rtt, factor, now);
+
+ UNLOCK(&adb->entrylocks[bucket]);
+}
+
+void
+dns_adb_agesrtt(dns_adb_t *adb, dns_adbaddrinfo_t *addr, isc_stdtime_t now) {
+ int bucket;
+
+ REQUIRE(DNS_ADB_VALID(adb));
+ REQUIRE(DNS_ADBADDRINFO_VALID(addr));
+
+ bucket = addr->entry->lock_bucket;
+ LOCK(&adb->entrylocks[bucket]);
+
+ adjustsrtt(addr, 0, DNS_ADB_RTTADJAGE, now);
+
+ UNLOCK(&adb->entrylocks[bucket]);
+}
+
+static void
+adjustsrtt(dns_adbaddrinfo_t *addr, unsigned int rtt, unsigned int factor,
+ isc_stdtime_t now)
+{
+ uint64_t new_srtt;
+
+ if (factor == DNS_ADB_RTTADJAGE) {
+ if (addr->entry->lastage != now) {
+ new_srtt = addr->entry->srtt;
+ new_srtt <<= 9;
+ new_srtt -= addr->entry->srtt;
+ new_srtt >>= 9;
+ addr->entry->lastage = now;
+ } else
+ new_srtt = addr->entry->srtt;
+ } else
+ new_srtt = ((uint64_t)addr->entry->srtt / 10 * factor)
+ + ((uint64_t)rtt / 10 * (10 - factor));
+
+ addr->entry->srtt = (unsigned int) new_srtt;
+ addr->srtt = (unsigned int) new_srtt;
+
+ if (addr->entry->expires == 0)
+ addr->entry->expires = now + ADB_ENTRY_WINDOW;
+}
+
+void
+dns_adb_changeflags(dns_adb_t *adb, dns_adbaddrinfo_t *addr,
+ unsigned int bits, unsigned int mask)
+{
+ int bucket;
+ isc_stdtime_t now;
+
+ REQUIRE(DNS_ADB_VALID(adb));
+ REQUIRE(DNS_ADBADDRINFO_VALID(addr));
+
+ REQUIRE((bits & ENTRY_IS_DEAD) == 0);
+ REQUIRE((mask & ENTRY_IS_DEAD) == 0);
+
+ bucket = addr->entry->lock_bucket;
+ LOCK(&adb->entrylocks[bucket]);
+
+ addr->entry->flags = (addr->entry->flags & ~mask) | (bits & mask);
+ if (addr->entry->expires == 0) {
+ isc_stdtime_get(&now);
+ addr->entry->expires = now + ADB_ENTRY_WINDOW;
+ }
+
+ /*
+ * 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]);
+}
+
+/*
+ * (10000 / ((10 + n) / 10)^(3/2)) for n in 0..99.
+ * These will be used to make quota adjustments.
+ */
+static int quota_adj[] = {
+ 10000, 8668, 7607, 6747, 6037, 5443, 4941, 4512, 4141,
+ 3818, 3536, 3286, 3065, 2867, 2690, 2530, 2385, 2254,
+ 2134, 2025, 1925, 1832, 1747, 1668, 1595, 1527, 1464,
+ 1405, 1350, 1298, 1250, 1205, 1162, 1121, 1083, 1048,
+ 1014, 981, 922, 894, 868, 843, 820, 797, 775, 755,
+ 735, 716, 698, 680, 664, 648, 632, 618, 603, 590, 577,
+ 564, 552, 540, 529, 518, 507, 497, 487, 477, 468, 459,
+ 450, 442, 434, 426, 418, 411, 404, 397, 390, 383, 377,
+ 370, 364, 358, 353, 347, 342, 336, 331, 326, 321, 316,
+ 312, 307, 303, 298, 294, 290, 286, 282, 278
+};
+
+#define QUOTA_ADJ_SIZE (sizeof(quota_adj)/sizeof(quota_adj[0]))
+
+/*
+ * Caller must hold adbentry lock
+ */
+static void
+maybe_adjust_quota(dns_adb_t *adb, dns_adbaddrinfo_t *addr,
+ bool timeout)
+{
+ double tr;
+
+ UNUSED(adb);
+
+ if (adb->quota == 0 || adb->atr_freq == 0)
+ return;
+
+ if (timeout)
+ addr->entry->timeouts++;
+
+ if (addr->entry->completed++ <= adb->atr_freq)
+ return;
+
+ /*
+ * Calculate an exponential rolling average of the timeout ratio
+ *
+ * XXX: Integer arithmetic might be better than floating point
+ */
+ tr = (double) addr->entry->timeouts / addr->entry->completed;
+ addr->entry->timeouts = addr->entry->completed = 0;
+ INSIST(addr->entry->atr >= 0.0);
+ INSIST(addr->entry->atr <= 1.0);
+ INSIST(adb->atr_discount >= 0.0);
+ INSIST(adb->atr_discount <= 1.0);
+ addr->entry->atr *= 1.0 - adb->atr_discount;
+ addr->entry->atr += tr * adb->atr_discount;
+ addr->entry->atr = ISC_CLAMP(addr->entry->atr, 0.0, 1.0);
+
+ if (addr->entry->atr < adb->atr_low && addr->entry->mode > 0) {
+ addr->entry->quota = adb->quota *
+ quota_adj[--addr->entry->mode] / 10000;
+ log_quota(addr->entry, "atr %0.2f, quota increased to %u",
+ addr->entry->atr, addr->entry->quota);
+ } else if (addr->entry->atr > adb->atr_high &&
+ addr->entry->mode < (QUOTA_ADJ_SIZE - 1)) {
+ addr->entry->quota = adb->quota *
+ quota_adj[++addr->entry->mode] / 10000;
+ log_quota(addr->entry, "atr %0.2f, quota decreased to %u",
+ addr->entry->atr, addr->entry->quota);
+ }
+
+ /* Ensure we don't drop to zero */
+ if (addr->entry->quota == 0)
+ addr->entry->quota = 1;
+}
+
+#define EDNSTOS 3U
+bool
+dns_adb_noedns(dns_adb_t *adb, dns_adbaddrinfo_t *addr) {
+ int bucket;
+ bool noedns = false;
+
+ REQUIRE(DNS_ADB_VALID(adb));
+ REQUIRE(DNS_ADBADDRINFO_VALID(addr));
+
+ bucket = addr->entry->lock_bucket;
+ LOCK(&adb->entrylocks[bucket]);
+
+ if (addr->entry->edns == 0U &&
+ (addr->entry->plain > EDNSTOS || addr->entry->to4096 > EDNSTOS)) {
+ if (((addr->entry->plain + addr->entry->to4096) & 0x3f) != 0) {
+ noedns = true;
+ } else {
+ /*
+ * Increment plain so we don't get stuck.
+ */
+ addr->entry->plain++;
+ if (addr->entry->plain == 0xff) {
+ addr->entry->edns >>= 1;
+ addr->entry->to4096 >>= 1;
+ addr->entry->to1432 >>= 1;
+ addr->entry->to1232 >>= 1;
+ addr->entry->to512 >>= 1;
+ addr->entry->plain >>= 1;
+ addr->entry->plainto >>= 1;
+ }
+ }
+ }
+ UNLOCK(&adb->entrylocks[bucket]);
+ return (noedns);
+}
+
+void
+dns_adb_plainresponse(dns_adb_t *adb, dns_adbaddrinfo_t *addr) {
+ int bucket;
+
+ REQUIRE(DNS_ADB_VALID(adb));
+ REQUIRE(DNS_ADBADDRINFO_VALID(addr));
+
+ bucket = addr->entry->lock_bucket;
+ LOCK(&adb->entrylocks[bucket]);
+
+ maybe_adjust_quota(adb, addr, false);
+
+ addr->entry->plain++;
+ if (addr->entry->plain == 0xff) {
+ addr->entry->edns >>= 1;
+ addr->entry->to4096 >>= 1;
+ addr->entry->to1432 >>= 1;
+ addr->entry->to1232 >>= 1;
+ addr->entry->to512 >>= 1;
+ addr->entry->plain >>= 1;
+ addr->entry->plainto >>= 1;
+ }
+ UNLOCK(&adb->entrylocks[bucket]);
+}
+
+void
+dns_adb_timeout(dns_adb_t *adb, dns_adbaddrinfo_t *addr) {
+ int bucket;
+
+ REQUIRE(DNS_ADB_VALID(adb));
+ REQUIRE(DNS_ADBADDRINFO_VALID(addr));
+
+ bucket = addr->entry->lock_bucket;
+ LOCK(&adb->entrylocks[bucket]);
+
+ maybe_adjust_quota(adb, addr, true);
+
+ /*
+ * If we have not had a successful query then clear all
+ * edns timeout information.
+ */
+ if (addr->entry->edns == 0 && addr->entry->plain == 0) {
+ addr->entry->to512 = 0;
+ addr->entry->to1232 = 0;
+ addr->entry->to1432 = 0;
+ addr->entry->to4096 = 0;
+ } else {
+ addr->entry->to512 >>= 1;
+ addr->entry->to1232 >>= 1;
+ addr->entry->to1432 >>= 1;
+ addr->entry->to4096 >>= 1;
+ }
+
+ addr->entry->plainto++;
+ if (addr->entry->plainto == 0xff) {
+ addr->entry->edns >>= 1;
+ addr->entry->plain >>= 1;
+ addr->entry->plainto >>= 1;
+ }
+ UNLOCK(&adb->entrylocks[bucket]);
+}
+
+void
+dns_adb_ednsto(dns_adb_t *adb, dns_adbaddrinfo_t *addr, unsigned int size) {
+ int bucket;
+
+ REQUIRE(DNS_ADB_VALID(adb));
+ REQUIRE(DNS_ADBADDRINFO_VALID(addr));
+
+ bucket = addr->entry->lock_bucket;
+ LOCK(&adb->entrylocks[bucket]);
+
+ maybe_adjust_quota(adb, addr, true);
+
+ if (size <= 512U) {
+ if (addr->entry->to512 <= EDNSTOS) {
+ addr->entry->to512++;
+ addr->entry->to1232++;
+ addr->entry->to1432++;
+ addr->entry->to4096++;
+ }
+ } else if (size <= 1232U) {
+ if (addr->entry->to1232 <= EDNSTOS) {
+ addr->entry->to1232++;
+ addr->entry->to1432++;
+ addr->entry->to4096++;
+ }
+ } else if (size <= 1432U) {
+ if (addr->entry->to1432 <= EDNSTOS) {
+ addr->entry->to1432++;
+ addr->entry->to4096++;
+ }
+ } else {
+ if (addr->entry->to4096 <= EDNSTOS)
+ addr->entry->to4096++;
+ }
+
+ if (addr->entry->to4096 == 0xff) {
+ addr->entry->edns >>= 1;
+ addr->entry->to4096 >>= 1;
+ addr->entry->to1432 >>= 1;
+ addr->entry->to1232 >>= 1;
+ addr->entry->to512 >>= 1;
+ addr->entry->plain >>= 1;
+ addr->entry->plainto >>= 1;
+ }
+ UNLOCK(&adb->entrylocks[bucket]);
+}
+
+void
+dns_adb_setudpsize(dns_adb_t *adb, dns_adbaddrinfo_t *addr, unsigned int size) {
+ int bucket;
+
+ REQUIRE(DNS_ADB_VALID(adb));
+ REQUIRE(DNS_ADBADDRINFO_VALID(addr));
+
+ bucket = addr->entry->lock_bucket;
+ LOCK(&adb->entrylocks[bucket]);
+ if (size < 512U)
+ size = 512U;
+ if (size > addr->entry->udpsize)
+ addr->entry->udpsize = size;
+
+ maybe_adjust_quota(adb, addr, false);
+
+ addr->entry->edns++;
+ if (addr->entry->edns == 0xff) {
+ addr->entry->edns >>= 1;
+ addr->entry->to4096 >>= 1;
+ addr->entry->to1432 >>= 1;
+ addr->entry->to1232 >>= 1;
+ addr->entry->to512 >>= 1;
+ addr->entry->plain >>= 1;
+ addr->entry->plainto >>= 1;
+ }
+ UNLOCK(&adb->entrylocks[bucket]);
+}
+
+unsigned int
+dns_adb_getudpsize(dns_adb_t *adb, dns_adbaddrinfo_t *addr) {
+ int bucket;
+ unsigned int size;
+
+ REQUIRE(DNS_ADB_VALID(adb));
+ REQUIRE(DNS_ADBADDRINFO_VALID(addr));
+
+ bucket = addr->entry->lock_bucket;
+ LOCK(&adb->entrylocks[bucket]);
+ size = addr->entry->udpsize;
+ UNLOCK(&adb->entrylocks[bucket]);
+
+ return (size);
+}
+
+unsigned int
+dns_adb_probesize(dns_adb_t *adb, dns_adbaddrinfo_t *addr) {
+ return dns_adb_probesize2(adb, addr, 0);
+}
+
+unsigned int
+dns_adb_probesize2(dns_adb_t *adb, dns_adbaddrinfo_t *addr, int lookups) {
+ int bucket;
+ unsigned int size;
+
+ REQUIRE(DNS_ADB_VALID(adb));
+ REQUIRE(DNS_ADBADDRINFO_VALID(addr));
+
+ bucket = addr->entry->lock_bucket;
+ LOCK(&adb->entrylocks[bucket]);
+ if (addr->entry->to1232 > EDNSTOS || lookups >= 2)
+ size = 512;
+ else if (addr->entry->to1432 > EDNSTOS || lookups >= 1)
+ size = 1232;
+ else if (addr->entry->to4096 > EDNSTOS)
+ size = 1432;
+ else
+ size = 4096;
+ /*
+ * Don't shrink probe size below what we have seen due to multiple
+ * lookups.
+ */
+ if (lookups > 0 &&
+ size < addr->entry->udpsize && addr->entry->udpsize < 4096)
+ size = addr->entry->udpsize;
+ UNLOCK(&adb->entrylocks[bucket]);
+
+ return (size);
+}
+
+void
+dns_adb_setcookie(dns_adb_t *adb, dns_adbaddrinfo_t *addr,
+ const unsigned char *cookie, size_t len)
+{
+ int bucket;
+
+ REQUIRE(DNS_ADB_VALID(adb));
+ REQUIRE(DNS_ADBADDRINFO_VALID(addr));
+
+ bucket = addr->entry->lock_bucket;
+ LOCK(&adb->entrylocks[bucket]);
+
+ if (addr->entry->cookie != NULL &&
+ (cookie == NULL || len != addr->entry->cookielen)) {
+ isc_mem_put(adb->mctx, addr->entry->cookie,
+ addr->entry->cookielen);
+ addr->entry->cookie = NULL;
+ addr->entry->cookielen = 0;
+ }
+
+ if (addr->entry->cookie == NULL && cookie != NULL && len != 0U) {
+ addr->entry->cookie = isc_mem_get(adb->mctx, len);
+ if (addr->entry->cookie != NULL)
+ addr->entry->cookielen = (uint16_t)len;
+ }
+
+ if (addr->entry->cookie != NULL)
+ memmove(addr->entry->cookie, cookie, len);
+ UNLOCK(&adb->entrylocks[bucket]);
+}
+
+size_t
+dns_adb_getcookie(dns_adb_t *adb, dns_adbaddrinfo_t *addr,
+ unsigned char *cookie, size_t len)
+{
+ int bucket;
+
+ REQUIRE(DNS_ADB_VALID(adb));
+ REQUIRE(DNS_ADBADDRINFO_VALID(addr));
+
+ bucket = addr->entry->lock_bucket;
+ LOCK(&adb->entrylocks[bucket]);
+ if (cookie != NULL && addr->entry->cookie != NULL &&
+ len >= addr->entry->cookielen)
+ {
+ memmove(cookie, addr->entry->cookie, addr->entry->cookielen);
+ len = addr->entry->cookielen;
+ } else
+ len = 0;
+ UNLOCK(&adb->entrylocks[bucket]);
+
+ return (len);
+}
+
+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, now);
+ INSIST(bucket != DNS_ADB_INVALIDBUCKET);
+ 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, 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;
+ bool want_check_exit = false;
+ bool overmem;
+
+ REQUIRE(DNS_ADB_VALID(adb));
+ REQUIRE(addrp != NULL);
+ addr = *addrp;
+ REQUIRE(DNS_ADBADDRINFO_VALID(addr));
+ entry = addr->entry;
+ REQUIRE(DNS_ADBENTRY_VALID(entry));
+
+ *addrp = NULL;
+ overmem = isc_mem_isovermem(adb->mctx);
+
+ bucket = addr->entry->lock_bucket;
+ LOCK(&adb->entrylocks[bucket]);
+
+ if (entry->expires == 0) {
+ isc_stdtime_get(&now);
+ entry->expires = now + ADB_ENTRY_WINDOW;
+ }
+
+ want_check_exit = dec_entry_refcnt(adb, overmem, entry, 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 < adb->nnames; i++)
+ RUNTIME_CHECK(cleanup_names(adb, i, INT_MAX) == false);
+ for (i = 0; i < adb->nentries; i++)
+ RUNTIME_CHECK(cleanup_entries(adb, i, INT_MAX) == false);
+
+#ifdef DUMP_ADB_AFTER_CLEANING
+ dump_adb(adb, stdout, 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;
+
+ REQUIRE(DNS_ADB_VALID(adb));
+ REQUIRE(name != NULL);
+
+ LOCK(&adb->lock);
+ bucket = dns_name_hash(name, false) % adb->nnames;
+ 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) ==
+ false);
+ }
+ adbname = nextname;
+ }
+ UNLOCK(&adb->namelocks[bucket]);
+ UNLOCK(&adb->lock);
+}
+
+void
+dns_adb_flushnames(dns_adb_t *adb, dns_name_t *name) {
+ dns_adbname_t *adbname, *nextname;
+ unsigned int i;
+
+ REQUIRE(DNS_ADB_VALID(adb));
+ REQUIRE(name != NULL);
+
+ LOCK(&adb->lock);
+ for (i = 0; i < adb->nnames; i++) {
+ LOCK(&adb->namelocks[i]);
+ adbname = ISC_LIST_HEAD(adb->names[i]);
+ while (adbname != NULL) {
+ bool ret;
+ nextname = ISC_LIST_NEXT(adbname, plink);
+ if (!NAME_DEAD(adbname) &&
+ dns_name_issubdomain(&adbname->name, name))
+ {
+ ret = kill_name(&adbname,
+ DNS_EVENT_ADBCANCELED);
+ RUNTIME_CHECK(ret == false);
+ }
+ adbname = nextname;
+ }
+ UNLOCK(&adb->namelocks[i]);
+ }
+ UNLOCK(&adb->lock);
+}
+
+static void
+water(void *arg, int mark) {
+ /*
+ * We're going to change the way to handle overmem condition: use
+ * isc_mem_isovermem() instead of storing the state via this callback,
+ * since the latter way tends to cause race conditions.
+ * To minimize the change, and in case we re-enable the callback
+ * approach, however, keep this function at the moment.
+ */
+
+ dns_adb_t *adb = arg;
+ bool overmem = (mark == ISC_MEM_HIWATER);
+
+ REQUIRE(DNS_ADB_VALID(adb));
+
+ DP(ISC_LOG_DEBUG(1),
+ "adb reached %s water mark", overmem ? "high" : "low");
+}
+
+void
+dns_adb_setadbsize(dns_adb_t *adb, size_t size) {
+ size_t hiwater, lowater;
+
+ INSIST(DNS_ADB_VALID(adb));
+
+ if (size != 0U && size < DNS_ADB_MINADBSIZE)
+ size = DNS_ADB_MINADBSIZE;
+
+ hiwater = size - (size >> 3); /* Approximately 7/8ths. */
+ lowater = size - (size >> 2); /* Approximately 3/4ths. */
+
+ if (size == 0U || hiwater == 0U || lowater == 0U)
+ isc_mem_setwater(adb->mctx, water, adb, 0, 0);
+ else
+ isc_mem_setwater(adb->mctx, water, adb, hiwater, lowater);
+}
+
+void
+dns_adb_setquota(dns_adb_t *adb, uint32_t quota, uint32_t freq,
+ double low, double high, double discount)
+{
+ REQUIRE(DNS_ADB_VALID(adb));
+
+ adb->quota = quota;
+ adb->atr_freq = freq;
+ adb->atr_low = low;
+ adb->atr_high = high;
+ adb->atr_discount = discount;
+}
+
+bool
+dns_adbentry_overquota(dns_adbentry_t *entry) {
+ bool block;
+ REQUIRE(DNS_ADBENTRY_VALID(entry));
+ block = (entry->quota != 0 && entry->active >= entry->quota);
+ return (block);
+}
+
+void
+dns_adb_beginudpfetch(dns_adb_t *adb, dns_adbaddrinfo_t *addr) {
+ int bucket;
+
+ REQUIRE(DNS_ADB_VALID(adb));
+ REQUIRE(DNS_ADBADDRINFO_VALID(addr));
+
+ bucket = addr->entry->lock_bucket;
+
+ LOCK(&adb->entrylocks[bucket]);
+ addr->entry->active++;
+ UNLOCK(&adb->entrylocks[bucket]);
+}
+
+void
+dns_adb_endudpfetch(dns_adb_t *adb, dns_adbaddrinfo_t *addr) {
+ int bucket;
+
+ REQUIRE(DNS_ADB_VALID(adb));
+ REQUIRE(DNS_ADBADDRINFO_VALID(addr));
+
+ bucket = addr->entry->lock_bucket;
+
+ LOCK(&adb->entrylocks[bucket]);
+ if (addr->entry->active > 0)
+ addr->entry->active--;
+ UNLOCK(&adb->entrylocks[bucket]);
+}
diff --git a/lib/dns/api b/lib/dns/api
new file mode 100644
index 0000000..d94b0e6
--- /dev/null
+++ b/lib/dns/api
@@ -0,0 +1,13 @@
+# LIBINTERFACE ranges
+# 9.6: 50-59, 110-119
+# 9.7: 60-79
+# 9.8: 80-89, 120-129
+# 9.9: 90-109, 170-179
+# 9.9-sub: 130-139, 150-159, 200-209
+# 9.10: 140-149, 190-199
+# 9.10-sub: 180-189
+# 9.11: 160-169,1100-1199
+# 9.12: 1200-1299
+LIBINTERFACE = 1104
+LIBREVISION = 2
+LIBAGE = 0
diff --git a/lib/dns/badcache.c b/lib/dns/badcache.c
new file mode 100644
index 0000000..5c784be
--- /dev/null
+++ b/lib/dns/badcache.c
@@ -0,0 +1,440 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/buffer.h>
+#include <isc/log.h>
+#include <isc/hash.h>
+#include <isc/mem.h>
+#include <isc/mutex.h>
+#include <isc/platform.h>
+#include <isc/print.h>
+#include <isc/string.h>
+#include <isc/time.h>
+#include <isc/util.h>
+
+#include <dns/badcache.h>
+#include <dns/name.h>
+#include <dns/rdatatype.h>
+#include <dns/types.h>
+
+typedef struct dns_bcentry dns_bcentry_t;
+
+struct dns_badcache {
+ unsigned int magic;
+ isc_mutex_t lock;
+ isc_mem_t *mctx;
+
+ dns_bcentry_t **table;
+ unsigned int count;
+ unsigned int minsize;
+ unsigned int size;
+ unsigned int sweep;
+};
+
+#define BADCACHE_MAGIC ISC_MAGIC('B', 'd', 'C', 'a')
+#define VALID_BADCACHE(m) ISC_MAGIC_VALID(m, BADCACHE_MAGIC)
+
+struct dns_bcentry {
+ dns_bcentry_t * next;
+ dns_rdatatype_t type;
+ isc_time_t expire;
+ uint32_t flags;
+ unsigned int hashval;
+ dns_name_t name;
+};
+
+static isc_result_t
+badcache_resize(dns_badcache_t *bc, isc_time_t *now, bool grow);
+
+isc_result_t
+dns_badcache_init(isc_mem_t *mctx, unsigned int size, dns_badcache_t **bcp) {
+ isc_result_t result;
+ dns_badcache_t *bc = NULL;
+
+ REQUIRE(bcp != NULL && *bcp == NULL);
+ REQUIRE(mctx != NULL);
+
+ bc = isc_mem_get(mctx, sizeof(dns_badcache_t));
+ if (bc == NULL)
+ return (ISC_R_NOMEMORY);
+ memset(bc, 0, sizeof(dns_badcache_t));
+
+ isc_mem_attach(mctx, &bc->mctx);
+ result = isc_mutex_init(&bc->lock);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ bc->table = isc_mem_get(bc->mctx, sizeof(*bc->table) * size);
+ if (bc->table == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto destroy_lock;
+ }
+
+ bc->size = bc->minsize = size;
+ memset(bc->table, 0, bc->size * sizeof(dns_bcentry_t *));
+
+ bc->count = 0;
+ bc->sweep = 0;
+ bc->magic = BADCACHE_MAGIC;
+
+ *bcp = bc;
+ return (ISC_R_SUCCESS);
+
+ destroy_lock:
+ DESTROYLOCK(&bc->lock);
+ cleanup:
+ isc_mem_putanddetach(&bc->mctx, bc, sizeof(dns_badcache_t));
+ return (result);
+}
+
+void
+dns_badcache_destroy(dns_badcache_t **bcp) {
+ dns_badcache_t *bc;
+
+ REQUIRE(bcp != NULL && *bcp != NULL);
+ bc = *bcp;
+
+ dns_badcache_flush(bc);
+
+ bc->magic = 0;
+ DESTROYLOCK(&bc->lock);
+ isc_mem_put(bc->mctx, bc->table, sizeof(dns_bcentry_t *) * bc->size);
+ isc_mem_putanddetach(&bc->mctx, bc, sizeof(dns_badcache_t));
+ *bcp = NULL;
+}
+
+static isc_result_t
+badcache_resize(dns_badcache_t *bc, isc_time_t *now, bool grow) {
+ dns_bcentry_t **newtable, *bad, *next;
+ unsigned int newsize, i;
+
+ if (grow)
+ newsize = bc->size * 2 + 1;
+ else
+ newsize = (bc->size - 1) / 2;
+
+ newtable = isc_mem_get(bc->mctx, sizeof(dns_bcentry_t *) * newsize);
+ if (newtable == NULL)
+ return (ISC_R_NOMEMORY);
+ memset(newtable, 0, sizeof(dns_bcentry_t *) * newsize);
+
+ for (i = 0; bc->count > 0 && i < bc->size; i++) {
+ for (bad = bc->table[i]; bad != NULL; bad = next) {
+ next = bad->next;
+ if (isc_time_compare(&bad->expire, now) < 0) {
+ isc_mem_put(bc->mctx, bad,
+ sizeof(*bad) + bad->name.length);
+ bc->count--;
+ } else {
+ bad->next = newtable[bad->hashval % newsize];
+ newtable[bad->hashval % newsize] = bad;
+ }
+ }
+ bc->table[i] = NULL;
+ }
+
+ isc_mem_put(bc->mctx, bc->table, sizeof(*bc->table) * bc->size);
+ bc->size = newsize;
+ bc->table = newtable;
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_badcache_add(dns_badcache_t *bc, dns_name_t *name,
+ dns_rdatatype_t type, bool update,
+ uint32_t flags, isc_time_t *expire)
+{
+ isc_result_t result;
+ unsigned int i, hashval;
+ dns_bcentry_t *bad, *prev, *next;
+ isc_time_t now;
+
+ REQUIRE(VALID_BADCACHE(bc));
+ REQUIRE(name != NULL);
+ REQUIRE(expire != NULL);
+
+ LOCK(&bc->lock);
+
+ result = isc_time_now(&now);
+ if (result != ISC_R_SUCCESS)
+ isc_time_settoepoch(&now);
+
+ hashval = dns_name_hash(name, false);
+ i = hashval % bc->size;
+ prev = NULL;
+ for (bad = bc->table[i]; bad != NULL; bad = next) {
+ next = bad->next;
+ if (bad->type == type && dns_name_equal(name, &bad->name)) {
+ if (update) {
+ bad->expire = *expire;
+ bad->flags = flags;
+ }
+ break;
+ }
+ if (isc_time_compare(&bad->expire, &now) < 0) {
+ if (prev == NULL)
+ bc->table[i] = bad->next;
+ else
+ prev->next = bad->next;
+ isc_mem_put(bc->mctx, bad,
+ sizeof(*bad) + bad->name.length);
+ bc->count--;
+ } else
+ prev = bad;
+ }
+
+ if (bad == NULL) {
+ isc_buffer_t buffer;
+ bad = isc_mem_get(bc->mctx, sizeof(*bad) + name->length);
+ if (bad == NULL)
+ goto cleanup;
+ bad->type = type;
+ bad->hashval = hashval;
+ bad->expire = *expire;
+ bad->flags = flags;
+ isc_buffer_init(&buffer, bad + 1, name->length);
+ dns_name_init(&bad->name, NULL);
+ dns_name_copy(name, &bad->name, &buffer);
+ bad->next = bc->table[i];
+ bc->table[i] = bad;
+ bc->count++;
+ if (bc->count > bc->size * 8)
+ badcache_resize(bc, &now, true);
+ if (bc->count < bc->size * 2 && bc->size > bc->minsize)
+ badcache_resize(bc, &now, false);
+ } else
+ bad->expire = *expire;
+
+ cleanup:
+ UNLOCK(&bc->lock);
+}
+
+bool
+dns_badcache_find(dns_badcache_t *bc, dns_name_t *name,
+ dns_rdatatype_t type, uint32_t *flagp,
+ isc_time_t *now)
+{
+ dns_bcentry_t *bad, *prev, *next;
+ bool answer = false;
+ unsigned int i;
+
+ REQUIRE(VALID_BADCACHE(bc));
+ REQUIRE(name != NULL);
+ REQUIRE(now != NULL);
+
+ LOCK(&bc->lock);
+
+ /*
+ * XXXMUKS: dns_name_equal() is expensive as it does a
+ * octet-by-octet comparison, and it can be made better in two
+ * ways here. First, lowercase the names (use
+ * dns_name_downcase() instead of dns_name_copy() in
+ * dns_badcache_add()) so that dns_name_caseequal() can be used
+ * which the compiler will emit as SIMD instructions. Second,
+ * don't put multiple copies of the same name in the chain (or
+ * multiple names will have to be matched for equality), but use
+ * name->link to store the type specific part.
+ */
+
+ if (bc->count == 0)
+ goto skip;
+
+ i = dns_name_hash(name, false) % bc->size;
+ prev = NULL;
+ for (bad = bc->table[i]; bad != NULL; bad = next) {
+ next = bad->next;
+ /*
+ * Search the hash list. Clean out expired records as we go.
+ */
+ if (isc_time_compare(&bad->expire, now) < 0) {
+ if (prev != NULL)
+ prev->next = bad->next;
+ else
+ bc->table[i] = bad->next;
+
+ isc_mem_put(bc->mctx, bad, sizeof(*bad) +
+ bad->name.length);
+ bc->count--;
+ continue;
+ }
+ if (bad->type == type && dns_name_equal(name, &bad->name)) {
+ if (flagp != NULL)
+ *flagp = bad->flags;
+ answer = true;
+ break;
+ }
+ prev = bad;
+ }
+ skip:
+
+ /*
+ * Slow sweep to clean out stale records.
+ */
+ i = bc->sweep++ % bc->size;
+ bad = bc->table[i];
+ if (bad != NULL && isc_time_compare(&bad->expire, now) < 0) {
+ bc->table[i] = bad->next;
+ isc_mem_put(bc->mctx, bad, sizeof(*bad) + bad->name.length);
+ bc->count--;
+ }
+
+ UNLOCK(&bc->lock);
+ return (answer);
+}
+
+void
+dns_badcache_flush(dns_badcache_t *bc) {
+ dns_bcentry_t *entry, *next;
+ unsigned int i;
+
+ REQUIRE(VALID_BADCACHE(bc));
+
+ for (i = 0; bc->count > 0 && i < bc->size; i++) {
+ for (entry = bc->table[i]; entry != NULL; entry = next) {
+ next = entry->next;
+ isc_mem_put(bc->mctx, entry, sizeof(*entry) +
+ entry->name.length);
+ bc->count--;
+ }
+ bc->table[i] = NULL;
+ }
+}
+
+void
+dns_badcache_flushname(dns_badcache_t *bc, dns_name_t *name) {
+ dns_bcentry_t *bad, *prev, *next;
+ isc_result_t result;
+ isc_time_t now;
+ unsigned int i;
+
+ REQUIRE(VALID_BADCACHE(bc));
+ REQUIRE(name != NULL);
+
+ LOCK(&bc->lock);
+
+ result = isc_time_now(&now);
+ if (result != ISC_R_SUCCESS)
+ isc_time_settoepoch(&now);
+ i = dns_name_hash(name, false) % bc->size;
+ prev = NULL;
+ for (bad = bc->table[i]; bad != NULL; bad = next) {
+ int n;
+ next = bad->next;
+ n = isc_time_compare(&bad->expire, &now);
+ if (n < 0 || dns_name_equal(name, &bad->name)) {
+ if (prev == NULL)
+ bc->table[i] = bad->next;
+ else
+ prev->next = bad->next;
+
+ isc_mem_put(bc->mctx, bad, sizeof(*bad) +
+ bad->name.length);
+ bc->count--;
+ } else
+ prev = bad;
+ }
+
+ UNLOCK(&bc->lock);
+}
+
+void
+dns_badcache_flushtree(dns_badcache_t *bc, dns_name_t *name) {
+ dns_bcentry_t *bad, *prev, *next;
+ unsigned int i;
+ int n;
+ isc_time_t now;
+ isc_result_t result;
+
+ REQUIRE(VALID_BADCACHE(bc));
+ REQUIRE(name != NULL);
+
+ LOCK(&bc->lock);
+
+ result = isc_time_now(&now);
+ if (result != ISC_R_SUCCESS)
+ isc_time_settoepoch(&now);
+
+ for (i = 0; bc->count > 0 && i < bc->size; i++) {
+ prev = NULL;
+ for (bad = bc->table[i]; bad != NULL; bad = next) {
+ next = bad->next;
+ n = isc_time_compare(&bad->expire, &now);
+ if (n < 0 || dns_name_issubdomain(&bad->name, name)) {
+ if (prev == NULL)
+ bc->table[i] = bad->next;
+ else
+ prev->next = bad->next;
+
+ isc_mem_put(bc->mctx, bad, sizeof(*bad) +
+ bad->name.length);
+ bc->count--;
+ } else
+ prev = bad;
+ }
+ }
+
+ UNLOCK(&bc->lock);
+}
+
+
+void
+dns_badcache_print(dns_badcache_t *bc, const char *cachename, FILE *fp) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char typebuf[DNS_RDATATYPE_FORMATSIZE];
+ dns_bcentry_t *bad, *next, *prev;
+ isc_time_t now;
+ unsigned int i;
+ uint64_t t;
+
+ REQUIRE(VALID_BADCACHE(bc));
+ REQUIRE(cachename != NULL);
+ REQUIRE(fp != NULL);
+
+ LOCK(&bc->lock);
+ fprintf(fp, ";\n; %s\n;\n", cachename);
+
+ TIME_NOW(&now);
+ for (i = 0; bc->count > 0 && i < bc->size; i++) {
+ prev = NULL;
+ for (bad = bc->table[i]; bad != NULL; bad = next) {
+ next = bad->next;
+ if (isc_time_compare(&bad->expire, &now) < 0) {
+ if (prev != NULL)
+ prev->next = bad->next;
+ else
+ bc->table[i] = bad->next;
+
+ isc_mem_put(bc->mctx, bad, sizeof(*bad) +
+ bad->name.length);
+ bc->count--;
+ continue;
+ }
+ prev = bad;
+ dns_name_format(&bad->name, namebuf, sizeof(namebuf));
+ dns_rdatatype_format(bad->type, typebuf,
+ sizeof(typebuf));
+ t = isc_time_microdiff(&bad->expire, &now);
+ t /= 1000;
+ fprintf(fp, "; %s/%s [ttl "
+ "%" PRIu64 "]\n",
+ namebuf, typebuf, t);
+ }
+ }
+ UNLOCK(&bc->lock);
+}
diff --git a/lib/dns/byaddr.c b/lib/dns/byaddr.c
new file mode 100644
index 0000000..9eece30
--- /dev/null
+++ b/lib/dns/byaddr.c
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdbool.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...
+ */
+
+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, bool 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] & 0xffU),
+ (bytes[2] & 0xffU),
+ (bytes[1] & 0xffU),
+ (bytes[0] & 0xffU));
+ } else if (address->family == AF_INET6) {
+ size_t remaining;
+
+ cp = textname;
+ for (i = 15; i >= 0; i--) {
+ *cp++ = hex_digits[bytes[i] & 0x0f];
+ *cp++ = '.';
+ *cp++ = hex_digits[(bytes[i] >> 4) & 0x0f];
+ *cp++ = '.';
+ }
+ remaining = sizeof(textname) - (cp - textname);
+ if ((options & DNS_BYADDROPT_IPV6INT) != 0) {
+ strlcpy(cp, "ip6.int.", remaining);
+ } else {
+ strlcpy(cp, "ip6.arpa.", remaining);
+ }
+ } 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, 0, NULL));
+}
+
+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;
+ bool 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 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 = NULL;
+ isc_mem_attach(mctx, &byaddr->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 = 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_putanddetach(&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 = 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_putanddetach(&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..4701ff8
--- /dev/null
+++ b/lib/dns/cache.c
@@ -0,0 +1,1575 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/json.h>
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/string.h>
+#include <isc/stats.h>
+#include <isc/task.h>
+#include <isc/time.h>
+#include <isc/timer.h>
+#include <isc/util.h>
+#include <isc/xml.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>
+#include <dns/stats.h>
+
+#include "rbtdb.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 2097152U /*%< 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 encapsulates 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. */
+ bool overmem; /*% The cache is in an overmem state. */
+ bool replaceiterator;
+};
+
+/*%
+ * The actual cache object.
+ */
+
+struct dns_cache {
+ /* Unlocked. */
+ unsigned int magic;
+ isc_mutex_t lock;
+ isc_mutex_t filelock;
+ isc_mem_t *mctx; /* Main cache memory */
+ isc_mem_t *hmctx; /* Heap memory */
+ char *name;
+
+ /* 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;
+ size_t size;
+ isc_stats_t *stats;
+
+ /* 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 *cmctx, 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)
+{
+ return (dns_cache_create3(cmctx, cmctx, taskmgr, timermgr, rdclass, "",
+ db_type, db_argc, db_argv, cachep));
+}
+
+isc_result_t
+dns_cache_create2(isc_mem_t *cmctx, isc_taskmgr_t *taskmgr,
+ isc_timermgr_t *timermgr, dns_rdataclass_t rdclass,
+ const char *cachename, const char *db_type,
+ unsigned int db_argc, char **db_argv, dns_cache_t **cachep)
+{
+ return (dns_cache_create3(cmctx, cmctx, taskmgr, timermgr, rdclass,
+ cachename, db_type, db_argc, db_argv,
+ cachep));
+}
+
+isc_result_t
+dns_cache_create3(isc_mem_t *cmctx, isc_mem_t *hmctx, isc_taskmgr_t *taskmgr,
+ isc_timermgr_t *timermgr, dns_rdataclass_t rdclass,
+ const char *cachename, 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, extra = 0;
+ isc_task_t *dbtask;
+
+ REQUIRE(cachep != NULL);
+ REQUIRE(*cachep == NULL);
+ REQUIRE(cmctx != NULL);
+ REQUIRE(hmctx != NULL);
+ REQUIRE(cachename != NULL);
+
+ cache = isc_mem_get(cmctx, sizeof(*cache));
+ if (cache == NULL)
+ return (ISC_R_NOMEMORY);
+
+ cache->mctx = cache->hmctx = NULL;
+ isc_mem_attach(cmctx, &cache->mctx);
+ isc_mem_attach(hmctx, &cache->hmctx);
+
+ cache->name = NULL;
+ if (cachename != NULL) {
+ cache->name = isc_mem_strdup(cmctx, cachename);
+ if (cache->name == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup_mem;
+ }
+ }
+
+ 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->stats = NULL;
+ result = isc_stats_create(cmctx, &cache->stats,
+ dns_cachestatscounter_max);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_filelock;
+
+ cache->db_type = isc_mem_strdup(cmctx, db_type);
+ if (cache->db_type == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup_stats;
+ }
+
+ /*
+ * For databases of type "rbt" we pass hmctx to dns_db_create()
+ * via cache->db_argv, followed by the rest of the arguments in
+ * db_argv (of which there really shouldn't be any).
+ */
+ if (strcmp(cache->db_type, "rbt") == 0)
+ extra = 1;
+
+ cache->db_argc = db_argc + extra;
+ cache->db_argv = NULL;
+
+ if (cache->db_argc != 0) {
+ cache->db_argv = isc_mem_get(cmctx,
+ 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;
+
+ cache->db_argv[0] = (char *) hmctx;
+ for (i = extra; i < cache->db_argc; i++) {
+ cache->db_argv[i] = isc_mem_strdup(cmctx,
+ db_argv[i - extra]);
+ if (cache->db_argv[i] == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup_dbargv;
+ }
+ }
+ }
+
+ /*
+ * Create the database
+ */
+ cache->db = NULL;
+ result = cache_create_db(cache, &cache->db);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_dbargv;
+ if (taskmgr != NULL) {
+ dbtask = NULL;
+ result = isc_task_create(taskmgr, 1, &dbtask);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_db;
+
+ isc_task_setname(dbtask, "cache_dbtask", NULL);
+ dns_db_settask(cache->db, dbtask);
+ isc_task_detach(&dbtask);
+ }
+
+ 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;
+
+ result = dns_db_setcachestats(cache->db, cache->stats);
+ 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 = extra; i < cache->db_argc; i++)
+ if (cache->db_argv[i] != NULL)
+ isc_mem_free(cmctx, cache->db_argv[i]);
+ if (cache->db_argv != NULL)
+ isc_mem_put(cmctx, cache->db_argv,
+ cache->db_argc * sizeof(char *));
+ cleanup_dbtype:
+ isc_mem_free(cmctx, cache->db_type);
+ cleanup_filelock:
+ DESTROYLOCK(&cache->filelock);
+ cleanup_stats:
+ isc_stats_detach(&cache->stats);
+ cleanup_lock:
+ DESTROYLOCK(&cache->lock);
+ cleanup_mem:
+ if (cache->name != NULL)
+ isc_mem_free(cmctx, cache->name);
+ isc_mem_detach(&cache->hmctx);
+ isc_mem_putanddetach(&cache->mctx, cache, sizeof(*cache));
+ return (result);
+}
+
+static void
+cache_free(dns_cache_t *cache) {
+ 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) {
+ /*
+ * We don't free db_argv[0] in "rbt" cache databases
+ * as it's a pointer to hmctx
+ */
+ int extra = 0;
+ if (strcmp(cache->db_type, "rbt") == 0)
+ extra = 1;
+ for (i = extra; 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);
+
+ if (cache->name != NULL)
+ isc_mem_free(cache->mctx, cache->name);
+
+ if (cache->stats != NULL)
+ isc_stats_detach(&cache->stats);
+
+ DESTROYLOCK(&cache->lock);
+ DESTROYLOCK(&cache->filelock);
+
+ cache->magic = 0;
+ isc_mem_detach(&cache->hmctx);
+ isc_mem_putanddetach(&cache->mctx, cache, sizeof(*cache));
+}
+
+
+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;
+ bool free_cache = 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 = false;
+ free_cache = 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 = 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, true);
+ } else {
+ isc_interval_set(&interval, cache->cleaner.cleaning_interval,
+ 0);
+ result = isc_timer_reset(cache->cleaner.cleaning_timer,
+ isc_timertype_ticker,
+ NULL, &interval, 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);
+}
+
+unsigned int
+dns_cache_getcleaninginterval(dns_cache_t *cache) {
+ unsigned int t;
+
+ REQUIRE(VALID_CACHE(cache));
+
+ LOCK(&cache->lock);
+ t = cache->cleaner.cleaning_interval;
+ UNLOCK(&cache->lock);
+
+ return (t);
+}
+
+const char *
+dns_cache_getname(dns_cache_t *cache) {
+ REQUIRE(VALID_CACHE(cache));
+
+ return (cache->name);
+}
+
+/*
+ * 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 = false;
+ cleaner->replaceiterator = false;
+
+ cleaner->task = NULL;
+ cleaner->cleaning_timer = NULL;
+ cleaner->resched_event = NULL;
+ cleaner->overmem_event = NULL;
+ cleaner->cleaning_interval = 0; /* Initially turned off. */
+
+ result = dns_db_createiterator(cleaner->cache->db, 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;
+ }
+
+ 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, 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, 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;
+ bool want_cleaning = 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 = 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,
+ false,
+ &cleaner->iterator);
+ cleaner->replaceiterator = 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;
+ bool overmem = (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, size_t size) {
+ size_t hiwater, lowater;
+
+ REQUIRE(VALID_CACHE(cache));
+
+ /*
+ * Impose a minimum cache size; pathological things happen if there
+ * is too little room.
+ */
+ if (size != 0U && size < DNS_CACHE_MINSIZE)
+ size = DNS_CACHE_MINSIZE;
+
+ LOCK(&cache->lock);
+ cache->size = size;
+ UNLOCK(&cache->lock);
+
+ 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 == 0U || hiwater == 0U || lowater == 0U)
+ /*
+ * 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);
+}
+
+size_t
+dns_cache_getcachesize(dns_cache_t *cache) {
+ size_t size;
+
+ REQUIRE(VALID_CACHE(cache));
+
+ LOCK(&cache->lock);
+ size = cache->size;
+ UNLOCK(&cache->lock);
+
+ return (size);
+}
+
+/*
+ * 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;
+ bool should_free = 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 = 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, *olddb;
+ dns_dbiterator_t *dbiterator = NULL, *olddbiterator = NULL;
+ isc_result_t result;
+
+ result = cache_create_db(cache, &db);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = dns_db_createiterator(db, false, &dbiterator);
+ if (result != ISC_R_SUCCESS) {
+ dns_db_detach(&db);
+ return (result);
+ }
+
+ LOCK(&cache->lock);
+ LOCK(&cache->cleaner.lock);
+ if (cache->cleaner.state == cleaner_s_idle) {
+ olddbiterator = cache->cleaner.iterator;
+ cache->cleaner.iterator = dbiterator;
+ dbiterator = NULL;
+ } else {
+ if (cache->cleaner.state == cleaner_s_busy)
+ cache->cleaner.state = cleaner_s_done;
+ cache->cleaner.replaceiterator = true;
+ }
+ olddb = cache->db;
+ cache->db = db;
+ dns_db_setcachestats(cache->db, cache->stats);
+ UNLOCK(&cache->cleaner.lock);
+ UNLOCK(&cache->lock);
+
+ if (dbiterator != NULL)
+ dns_dbiterator_destroy(&dbiterator);
+ if (olddbiterator != NULL)
+ dns_dbiterator_destroy(&olddbiterator);
+ dns_db_detach(&olddb);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+clearnode(dns_db_t *db, dns_dbnode_t *node) {
+ isc_result_t result;
+ dns_rdatasetiter_t *iter = NULL;
+
+ result = dns_db_allrdatasets(db, node, NULL, (isc_stdtime_t)0, &iter);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ 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(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);
+ return (result);
+}
+
+static isc_result_t
+cleartree(dns_db_t *db, dns_name_t *name) {
+ isc_result_t result, answer = ISC_R_SUCCESS;
+ dns_dbiterator_t *iter = NULL;
+ dns_dbnode_t *node = NULL, *top = NULL;
+ dns_fixedname_t fnodename;
+ dns_name_t *nodename;
+
+ /*
+ * Create the node if it doesn't exist so dns_dbiterator_seek()
+ * can find it. We will continue even if this fails.
+ */
+ (void)dns_db_findnode(db, name, true, &top);
+
+ nodename = dns_fixedname_initname(&fnodename);
+
+ result = dns_db_createiterator(db, 0, &iter);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = dns_dbiterator_seek(iter, name);
+ if (result == DNS_R_PARTIALMATCH)
+ result = dns_dbiterator_next(iter);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ while (result == ISC_R_SUCCESS) {
+ result = dns_dbiterator_current(iter, &node, nodename);
+ if (result == DNS_R_NEWORIGIN)
+ result = ISC_R_SUCCESS;
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ /*
+ * Are we done?
+ */
+ if (! dns_name_issubdomain(nodename, name))
+ goto cleanup;
+
+ /*
+ * If clearnode fails record and move onto the next node.
+ */
+ result = clearnode(db, node);
+ if (result != ISC_R_SUCCESS && answer == ISC_R_SUCCESS)
+ answer = result;
+ dns_db_detachnode(db, &node);
+ result = dns_dbiterator_next(iter);
+ }
+
+ cleanup:
+ if (result == ISC_R_NOMORE || result == ISC_R_NOTFOUND)
+ result = ISC_R_SUCCESS;
+ if (result != ISC_R_SUCCESS && answer == ISC_R_SUCCESS)
+ answer = result;
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ if (iter != NULL)
+ dns_dbiterator_destroy(&iter);
+ if (top != NULL)
+ dns_db_detachnode(db, &top);
+
+ return (answer);
+}
+
+isc_result_t
+dns_cache_flushname(dns_cache_t *cache, dns_name_t *name) {
+ return (dns_cache_flushnode(cache, name, false));
+}
+
+isc_result_t
+dns_cache_flushnode(dns_cache_t *cache, dns_name_t *name,
+ bool tree)
+{
+ isc_result_t result;
+ dns_dbnode_t *node = NULL;
+ dns_db_t *db = NULL;
+
+ if (tree && dns_name_equal(name, dns_rootname))
+ return (dns_cache_flush(cache));
+
+ LOCK(&cache->lock);
+ if (cache->db != NULL)
+ dns_db_attach(cache->db, &db);
+ UNLOCK(&cache->lock);
+ if (db == NULL)
+ return (ISC_R_SUCCESS);
+
+ if (tree) {
+ result = cleartree(cache->db, name);
+ } else {
+ result = dns_db_findnode(cache->db, name, false, &node);
+ if (result == ISC_R_NOTFOUND) {
+ result = ISC_R_SUCCESS;
+ goto cleanup_db;
+ }
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_db;
+ result = clearnode(cache->db, node);
+ dns_db_detachnode(cache->db, &node);
+ }
+
+ cleanup_db:
+ dns_db_detach(&db);
+ return (result);
+}
+
+isc_stats_t *
+dns_cache_getstats(dns_cache_t *cache) {
+ REQUIRE(VALID_CACHE(cache));
+ return (cache->stats);
+}
+
+void
+dns_cache_updatestats(dns_cache_t *cache, isc_result_t result) {
+ REQUIRE(VALID_CACHE(cache));
+ if (cache->stats == NULL)
+ return;
+
+ switch (result) {
+ case ISC_R_SUCCESS:
+ case DNS_R_NCACHENXDOMAIN:
+ case DNS_R_NCACHENXRRSET:
+ case DNS_R_CNAME:
+ case DNS_R_DNAME:
+ case DNS_R_GLUE:
+ case DNS_R_ZONECUT:
+ isc_stats_increment(cache->stats,
+ dns_cachestatscounter_queryhits);
+ break;
+ default:
+ isc_stats_increment(cache->stats,
+ dns_cachestatscounter_querymisses);
+ }
+}
+
+/*
+ * XXX: Much of the following code has been copied in from statschannel.c.
+ * We should refactor this into a generic function in stats.c that can be
+ * called from both places.
+ */
+typedef struct
+cache_dumparg {
+ isc_statsformat_t type;
+ void *arg; /* type dependent argument */
+ int ncounters; /* for general statistics */
+ int *counterindices; /* for general statistics */
+ uint64_t *countervalues; /* for general statistics */
+ isc_result_t result;
+} cache_dumparg_t;
+
+static void
+getcounter(isc_statscounter_t counter, uint64_t val, void *arg) {
+ cache_dumparg_t *dumparg = arg;
+
+ REQUIRE(counter < dumparg->ncounters);
+ dumparg->countervalues[counter] = val;
+}
+
+static void
+getcounters(isc_stats_t *stats, isc_statsformat_t type, int ncounters,
+ int *indices, uint64_t *values)
+{
+ cache_dumparg_t dumparg;
+
+ memset(values, 0, sizeof(values[0]) * ncounters);
+
+ dumparg.type = type;
+ dumparg.ncounters = ncounters;
+ dumparg.counterindices = indices;
+ dumparg.countervalues = values;
+
+ isc_stats_dump(stats, getcounter, &dumparg, ISC_STATSDUMP_VERBOSE);
+}
+
+void
+dns_cache_dumpstats(dns_cache_t *cache, FILE *fp) {
+ int indices[dns_cachestatscounter_max];
+ uint64_t values[dns_cachestatscounter_max];
+
+ REQUIRE(VALID_CACHE(cache));
+
+ getcounters(cache->stats, isc_statsformat_file,
+ dns_cachestatscounter_max, indices, values);
+
+ fprintf(fp, "%20" PRIu64 " %s\n",
+ values[dns_cachestatscounter_hits],
+ "cache hits");
+ fprintf(fp, "%20" PRIu64 " %s\n",
+ values[dns_cachestatscounter_misses],
+ "cache misses");
+ fprintf(fp, "%20" PRIu64 " %s\n",
+ values[dns_cachestatscounter_queryhits],
+ "cache hits (from query)");
+ fprintf(fp, "%20" PRIu64 " %s\n",
+ values[dns_cachestatscounter_querymisses],
+ "cache misses (from query)");
+ fprintf(fp, "%20" PRIu64 " %s\n",
+ values[dns_cachestatscounter_deletelru],
+ "cache records deleted due to memory exhaustion");
+ fprintf(fp, "%20" PRIu64 " %s\n",
+ values[dns_cachestatscounter_deletettl],
+ "cache records deleted due to TTL expiration");
+ fprintf(fp, "%20u %s\n", dns_db_nodecount(cache->db),
+ "cache database nodes");
+ fprintf(fp, "%20" PRIu64 " %s\n",
+ (uint64_t) dns_db_hashsize(cache->db),
+ "cache database hash buckets");
+
+ fprintf(fp, "%20" PRIu64 " %s\n",
+ (uint64_t) isc_mem_total(cache->mctx),
+ "cache tree memory total");
+ fprintf(fp, "%20" PRIu64 " %s\n",
+ (uint64_t) isc_mem_inuse(cache->mctx),
+ "cache tree memory in use");
+ fprintf(fp, "%20" PRIu64 " %s\n",
+ (uint64_t) isc_mem_maxinuse(cache->mctx),
+ "cache tree highest memory in use");
+
+ fprintf(fp, "%20" PRIu64 " %s\n",
+ (uint64_t) isc_mem_total(cache->hmctx),
+ "cache heap memory total");
+ fprintf(fp, "%20" PRIu64 " %s\n",
+ (uint64_t) isc_mem_inuse(cache->hmctx),
+ "cache heap memory in use");
+ fprintf(fp, "%20" PRIu64 " %s\n",
+ (uint64_t) isc_mem_maxinuse(cache->hmctx),
+ "cache heap highest memory in use");
+}
+
+#ifdef HAVE_LIBXML2
+#define TRY0(a) do { xmlrc = (a); if (xmlrc < 0) goto error; } while(0)
+static int
+renderstat(const char *name, uint64_t value, xmlTextWriterPtr writer) {
+ int xmlrc;
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counter"));
+ TRY0(xmlTextWriterWriteAttribute(writer,
+ ISC_XMLCHAR "name", ISC_XMLCHAR name));
+ TRY0(xmlTextWriterWriteFormatString(writer,
+ "%" PRIu64,
+ value));
+ TRY0(xmlTextWriterEndElement(writer)); /* counter */
+
+error:
+ return (xmlrc);
+}
+
+int
+dns_cache_renderxml(dns_cache_t *cache, xmlTextWriterPtr writer) {
+ int indices[dns_cachestatscounter_max];
+ uint64_t values[dns_cachestatscounter_max];
+ int xmlrc;
+
+ REQUIRE(VALID_CACHE(cache));
+
+ getcounters(cache->stats, isc_statsformat_file,
+ dns_cachestatscounter_max, indices, values);
+ TRY0(renderstat("CacheHits",
+ values[dns_cachestatscounter_hits], writer));
+ TRY0(renderstat("CacheMisses",
+ values[dns_cachestatscounter_misses], writer));
+ TRY0(renderstat("QueryHits",
+ values[dns_cachestatscounter_queryhits], writer));
+ TRY0(renderstat("QueryMisses",
+ values[dns_cachestatscounter_querymisses], writer));
+ TRY0(renderstat("DeleteLRU",
+ values[dns_cachestatscounter_deletelru], writer));
+ TRY0(renderstat("DeleteTTL",
+ values[dns_cachestatscounter_deletettl], writer));
+
+ TRY0(renderstat("CacheNodes", dns_db_nodecount(cache->db), writer));
+ TRY0(renderstat("CacheBuckets", dns_db_hashsize(cache->db), writer));
+
+ TRY0(renderstat("TreeMemTotal", isc_mem_total(cache->mctx), writer));
+ TRY0(renderstat("TreeMemInUse", isc_mem_inuse(cache->mctx), writer));
+ TRY0(renderstat("TreeMemMax", isc_mem_maxinuse(cache->mctx), writer));
+
+ TRY0(renderstat("HeapMemTotal", isc_mem_total(cache->hmctx), writer));
+ TRY0(renderstat("HeapMemInUse", isc_mem_inuse(cache->hmctx), writer));
+ TRY0(renderstat("HeapMemMax", isc_mem_maxinuse(cache->hmctx), writer));
+error:
+ return (xmlrc);
+}
+#endif
+
+#ifdef HAVE_JSON
+#define CHECKMEM(m) do { \
+ if (m == NULL) { \
+ result = ISC_R_NOMEMORY;\
+ goto error;\
+ } \
+} while(0)
+
+isc_result_t
+dns_cache_renderjson(dns_cache_t *cache, json_object *cstats) {
+ isc_result_t result = ISC_R_SUCCESS;
+ int indices[dns_cachestatscounter_max];
+ uint64_t values[dns_cachestatscounter_max];
+ json_object *obj;
+
+ REQUIRE(VALID_CACHE(cache));
+
+ getcounters(cache->stats, isc_statsformat_file,
+ dns_cachestatscounter_max, indices, values);
+
+ obj = json_object_new_int64(values[dns_cachestatscounter_hits]);
+ CHECKMEM(obj);
+ json_object_object_add(cstats, "CacheHits", obj);
+
+ obj = json_object_new_int64(values[dns_cachestatscounter_misses]);
+ CHECKMEM(obj);
+ json_object_object_add(cstats, "CacheMisses", obj);
+
+ obj = json_object_new_int64(values[dns_cachestatscounter_queryhits]);
+ CHECKMEM(obj);
+ json_object_object_add(cstats, "QueryHits", obj);
+
+ obj = json_object_new_int64(values[dns_cachestatscounter_querymisses]);
+ CHECKMEM(obj);
+ json_object_object_add(cstats, "QueryMisses", obj);
+
+ obj = json_object_new_int64(values[dns_cachestatscounter_deletelru]);
+ CHECKMEM(obj);
+ json_object_object_add(cstats, "DeleteLRU", obj);
+
+ obj = json_object_new_int64(values[dns_cachestatscounter_deletettl]);
+ CHECKMEM(obj);
+ json_object_object_add(cstats, "DeleteTTL", obj);
+
+ obj = json_object_new_int64(dns_db_nodecount(cache->db));
+ CHECKMEM(obj);
+ json_object_object_add(cstats, "CacheNodes", obj);
+
+ obj = json_object_new_int64(dns_db_hashsize(cache->db));
+ CHECKMEM(obj);
+ json_object_object_add(cstats, "CacheBuckets", obj);
+
+ obj = json_object_new_int64(isc_mem_total(cache->mctx));
+ CHECKMEM(obj);
+ json_object_object_add(cstats, "TreeMemTotal", obj);
+
+ obj = json_object_new_int64(isc_mem_inuse(cache->mctx));
+ CHECKMEM(obj);
+ json_object_object_add(cstats, "TreeMemInUse", obj);
+
+ obj = json_object_new_int64(isc_mem_maxinuse(cache->mctx));
+ CHECKMEM(obj);
+ json_object_object_add(cstats, "TreeMemMax", obj);
+
+ obj = json_object_new_int64(isc_mem_total(cache->hmctx));
+ CHECKMEM(obj);
+ json_object_object_add(cstats, "HeapMemTotal", obj);
+
+ obj = json_object_new_int64(isc_mem_inuse(cache->hmctx));
+ CHECKMEM(obj);
+ json_object_object_add(cstats, "HeapMemInUse", obj);
+
+ obj = json_object_new_int64(isc_mem_maxinuse(cache->hmctx));
+ CHECKMEM(obj);
+ json_object_object_add(cstats, "HeapMemMax", obj);
+
+ result = ISC_R_SUCCESS;
+error:
+ return (result);
+}
+#endif
diff --git a/lib/dns/callbacks.c b/lib/dns/callbacks.c
new file mode 100644
index 0000000..42dae51
--- /dev/null
+++ b/lib/dns/callbacks.c
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/print.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->magic = DNS_CALLBACK_MAGIC;
+ callbacks->add = NULL;
+ callbacks->rawdata = NULL;
+ callbacks->zone = 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/catz.c b/lib/dns/catz.c
new file mode 100644
index 0000000..b750d66
--- /dev/null
+++ b/lib/dns/catz.c
@@ -0,0 +1,1992 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/hex.h>
+#include <isc/mem.h>
+#include <isc/parseint.h>
+#include <isc/print.h>
+#include <isc/result.h>
+#include <isc/sha2.h>
+#include <isc/task.h>
+#include <isc/util.h>
+
+#include <dns/catz.h>
+#include <dns/dbiterator.h>
+#include <dns/events.h>
+#include <dns/rdatasetiter.h>
+#include <dns/view.h>
+#include <dns/zone.h>
+
+
+/*%
+ * Single member zone in a catalog
+ */
+struct dns_catz_entry {
+ dns_name_t name;
+ dns_catz_options_t opts;
+ isc_refcount_t refs;
+};
+
+/*%
+ * Catalog zone
+ */
+struct dns_catz_zone {
+ dns_name_t name;
+ dns_catz_zones_t *catzs;
+ dns_rdata_t soa;
+ /* key in entries is 'mhash', not domain name! */
+ isc_ht_t *entries;
+ /*
+ * defoptions are taken from named.conf
+ * zoneoptions are global options from zone
+ */
+ dns_catz_options_t defoptions;
+ dns_catz_options_t zoneoptions;
+ isc_time_t lastupdated;
+ bool updatepending;
+ uint32_t version;
+
+ dns_db_t *db;
+ dns_dbversion_t *dbversion;
+
+ isc_timer_t *updatetimer;
+ isc_event_t updateevent;
+
+ bool active;
+ bool db_registered;
+
+ isc_refcount_t refs;
+};
+
+static isc_result_t
+catz_process_zones_entry(dns_catz_zone_t *zone, dns_rdataset_t *value,
+ dns_label_t *mhash);
+static isc_result_t
+catz_process_zones_suboption(dns_catz_zone_t *zone, dns_rdataset_t *value,
+ dns_label_t *mhash, dns_name_t *name);
+
+/*%
+ * Collection of catalog zones for a view
+ */
+struct dns_catz_zones {
+ isc_ht_t *zones;
+ isc_mem_t *mctx;
+ isc_refcount_t refs;
+ isc_mutex_t lock;
+ dns_catz_zonemodmethods_t *zmm;
+ isc_taskmgr_t *taskmgr;
+ isc_timermgr_t *timermgr;
+ dns_view_t *view;
+ isc_task_t *updater;
+};
+
+void
+dns_catz_options_init(dns_catz_options_t *options) {
+ dns_ipkeylist_init(&options->masters);
+
+ options->allow_query = NULL;
+ options->allow_transfer = NULL;
+
+ options->allow_query = NULL;
+ options->allow_transfer = NULL;
+
+ options->in_memory = false;
+ options->min_update_interval = 5;
+ options->zonedir = NULL;
+}
+
+void
+dns_catz_options_free(dns_catz_options_t *options, isc_mem_t *mctx) {
+ if (options->masters.count != 0)
+ dns_ipkeylist_clear(mctx, &options->masters);
+ if (options->zonedir != NULL) {
+ isc_mem_free(mctx, options->zonedir);
+ options->zonedir = NULL;
+ }
+ if (options->allow_query != NULL)
+ isc_buffer_free(&options->allow_query);
+ if (options->allow_transfer != NULL)
+ isc_buffer_free(&options->allow_transfer);
+}
+
+isc_result_t
+dns_catz_options_copy(isc_mem_t *mctx, const dns_catz_options_t *src,
+ dns_catz_options_t *dst)
+{
+ REQUIRE(src != NULL);
+ REQUIRE(dst != NULL);
+ REQUIRE(dst->masters.count == 0);
+ REQUIRE(dst->allow_query == NULL);
+ REQUIRE(dst->allow_transfer == NULL);
+
+ if (src->masters.count != 0)
+ dns_ipkeylist_copy(mctx, &src->masters, &dst->masters);
+
+ if (dst->zonedir != NULL) {
+ isc_mem_free(mctx, dst->zonedir);
+ dst->zonedir = NULL;
+ }
+
+ if (src->zonedir != NULL)
+ dst->zonedir = isc_mem_strdup(mctx, src->zonedir);
+
+ if (src->allow_query != NULL)
+ isc_buffer_dup(mctx, &dst->allow_query, src->allow_query);
+
+ if (src->allow_transfer != NULL)
+ isc_buffer_dup(mctx, &dst->allow_transfer, src->allow_transfer);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_catz_options_setdefault(isc_mem_t *mctx, const dns_catz_options_t *defaults,
+ dns_catz_options_t *opts)
+{
+ if (opts->masters.count == 0 && defaults->masters.count != 0)
+ dns_ipkeylist_copy(mctx, &defaults->masters, &opts->masters);
+
+ if (defaults->zonedir != NULL)
+ opts->zonedir = isc_mem_strdup(mctx, defaults->zonedir);
+
+ if (opts->allow_query == NULL && defaults->allow_query != NULL)
+ isc_buffer_dup(mctx, &opts->allow_query, defaults->allow_query);
+ if (opts->allow_transfer == NULL && defaults->allow_transfer != NULL)
+ isc_buffer_dup(mctx, &opts->allow_transfer,
+ defaults->allow_transfer);
+
+ /* This option is always taken from config, so it's always 'default' */
+ opts->in_memory = defaults->in_memory;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_catz_entry_new(isc_mem_t *mctx, const dns_name_t *domain,
+ dns_catz_entry_t **nentryp)
+{
+ dns_catz_entry_t *nentry;
+ isc_result_t result;
+
+ REQUIRE(nentryp != NULL && *nentryp == NULL);
+
+ nentry = isc_mem_get(mctx, sizeof(dns_catz_entry_t));
+ if (nentry == NULL)
+ return (ISC_R_NOMEMORY);
+
+ dns_name_init(&nentry->name, NULL);
+ if (domain != NULL) {
+ result = dns_name_dup(domain, mctx, &nentry->name);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+
+ dns_catz_options_init(&nentry->opts);
+ isc_refcount_init(&nentry->refs, 1);
+ *nentryp = nentry;
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ isc_mem_put(mctx, nentry, sizeof(dns_catz_entry_t));
+ return (result);
+}
+
+dns_name_t *
+dns_catz_entry_getname(dns_catz_entry_t *entry) {
+ return (&entry->name);
+}
+
+isc_result_t
+dns_catz_entry_copy(dns_catz_zone_t *zone, const dns_catz_entry_t *entry,
+ dns_catz_entry_t **nentryp)
+{
+ isc_result_t result;
+ dns_catz_entry_t *nentry = NULL;
+
+ result = dns_catz_entry_new(zone->catzs->mctx, &entry->name, &nentry);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = dns_catz_options_copy(zone->catzs->mctx, &entry->opts,
+ &nentry->opts);
+ if (result != ISC_R_SUCCESS)
+ dns_catz_entry_detach(zone, &nentry);
+
+ *nentryp = nentry;
+ return (result);
+}
+
+void
+dns_catz_entry_attach(dns_catz_entry_t *entry, dns_catz_entry_t **entryp) {
+ REQUIRE(entryp != NULL && *entryp == NULL);
+ isc_refcount_increment(&entry->refs, NULL);
+ *entryp = entry;
+}
+
+void
+dns_catz_entry_detach(dns_catz_zone_t *zone, dns_catz_entry_t **entryp) {
+ dns_catz_entry_t *entry;
+ isc_mem_t *mctx;
+ unsigned int refs;
+
+ REQUIRE(entryp != NULL && *entryp != NULL);
+
+ entry = *entryp;
+ *entryp = NULL;
+
+ mctx = zone->catzs->mctx;
+
+ isc_refcount_decrement(&entry->refs, &refs);
+ if (refs == 0) {
+ dns_catz_options_free(&entry->opts, mctx);
+ if (dns_name_dynamic(&entry->name))
+ dns_name_free(&entry->name, mctx);
+ isc_refcount_destroy(&entry->refs);
+ isc_mem_put(mctx, entry, sizeof(dns_catz_entry_t));
+ }
+}
+
+bool
+dns_catz_entry_validate(const dns_catz_entry_t *entry) {
+ UNUSED(entry);
+
+ return (true);
+}
+
+bool
+dns_catz_entry_cmp(const dns_catz_entry_t *ea, const dns_catz_entry_t *eb) {
+ isc_region_t ra, rb;
+
+ if (ea == eb)
+ return (true);
+
+ if (ea->opts.masters.count != eb->opts.masters.count)
+ return (false);
+
+ if (memcmp(ea->opts.masters.addrs, eb->opts.masters.addrs,
+ ea->opts.masters.count * sizeof(isc_sockaddr_t)))
+ return (false);
+
+ /* If one is NULL and the other isn't, the entries don't match */
+ if ((ea->opts.allow_query == NULL) != (eb->opts.allow_query == NULL))
+ return (false);
+
+ /* If one is non-NULL, then they both are */
+ if (ea->opts.allow_query != NULL) {
+ isc_buffer_usedregion(ea->opts.allow_query, &ra);
+ isc_buffer_usedregion(eb->opts.allow_query, &rb);
+ if (isc_region_compare(&ra, &rb))
+ return (false);
+ }
+
+ /* Repeat the above checks with allow_transfer */
+ if ((ea->opts.allow_transfer == NULL) !=
+ (eb->opts.allow_transfer == NULL))
+ return (false);
+
+ if (ea->opts.allow_transfer != NULL) {
+ isc_buffer_usedregion(ea->opts.allow_transfer, &ra);
+ isc_buffer_usedregion(eb->opts.allow_transfer, &rb);
+ if (isc_region_compare(&ra, &rb))
+ return (false);
+ }
+
+ /* xxxwpk TODO compare dscps/keys! */
+ return (true);
+}
+
+dns_name_t *
+dns_catz_zone_getname(dns_catz_zone_t *zone) {
+ REQUIRE(zone != NULL);
+
+ return (&zone->name);
+}
+
+dns_catz_options_t *
+dns_catz_zone_getdefoptions(dns_catz_zone_t *zone) {
+ REQUIRE(zone != NULL);
+
+ return (&zone->defoptions);
+}
+
+void
+dns_catz_zone_resetdefoptions(dns_catz_zone_t *zone) {
+ REQUIRE(zone != NULL);
+
+ dns_catz_options_free(&zone->defoptions, zone->catzs->mctx);
+ dns_catz_options_init(&zone->defoptions);
+}
+
+isc_result_t
+dns_catz_zones_merge(dns_catz_zone_t *target, dns_catz_zone_t *newzone) {
+ isc_result_t result;
+ isc_ht_iter_t *iter1 = NULL, *iter2 = NULL;
+ isc_ht_iter_t *iteradd = NULL, *itermod = NULL;
+ isc_ht_t *toadd = NULL, *tomod = NULL;
+ bool delcur = false;
+ char czname[DNS_NAME_FORMATSIZE];
+ char zname[DNS_NAME_FORMATSIZE];
+ dns_catz_zoneop_fn_t addzone, modzone, delzone;
+
+ REQUIRE(target != NULL);
+ REQUIRE(newzone != NULL);
+
+ /* TODO verify the new zone first! */
+
+ addzone = target->catzs->zmm->addzone;
+ modzone = target->catzs->zmm->modzone;
+ delzone = target->catzs->zmm->delzone;
+
+ /* Copy zoneoptions from newzone into target. */
+
+ dns_catz_options_free(&target->zoneoptions, target->catzs->mctx);
+ dns_catz_options_copy(target->catzs->mctx, &newzone->zoneoptions,
+ &target->zoneoptions);
+ dns_catz_options_setdefault(target->catzs->mctx, &target->defoptions,
+ &target->zoneoptions);
+
+ dns_name_format(&target->name, czname, DNS_NAME_FORMATSIZE);
+
+ result = isc_ht_init(&toadd, target->catzs->mctx, 16);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = isc_ht_init(&tomod, target->catzs->mctx, 16);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = isc_ht_iter_create(newzone->entries, &iter1);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = isc_ht_iter_create(target->entries, &iter2);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ /*
+ * We can create those iterators now, even though toadd and tomod are
+ * empty
+ */
+ result = isc_ht_iter_create(toadd, &iteradd);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = isc_ht_iter_create(tomod, &itermod);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ /*
+ * First - walk the new zone and find all nodes that are not in the
+ * old zone, or are in both zones and are modified.
+ */
+ for (result = isc_ht_iter_first(iter1);
+ result == ISC_R_SUCCESS;
+ result = delcur ? isc_ht_iter_delcurrent_next(iter1) :
+ isc_ht_iter_next(iter1))
+ {
+ dns_catz_entry_t *nentry;
+ dns_catz_entry_t *oentry;
+ unsigned char * key;
+ size_t keysize;
+ delcur = false;
+
+ isc_ht_iter_current(iter1, (void **) &nentry);
+ isc_ht_iter_currentkey(iter1, &key, &keysize);
+
+ /*
+ * Spurious record that came from suboption without main
+ * record, removed.
+ * xxxwpk: make it a separate verification phase?
+ */
+ if (dns_name_countlabels(&nentry->name) == 0) {
+ dns_catz_entry_detach(newzone, &nentry);
+ delcur = true;
+ continue;
+ }
+
+ dns_name_format(&nentry->name, zname, DNS_NAME_FORMATSIZE);
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_MASTER, ISC_LOG_DEBUG(3),
+ "catz: iterating over '%s' from catalog '%s'",
+ zname, czname);
+ dns_catz_options_setdefault(target->catzs->mctx,
+ &target->zoneoptions,
+ &nentry->opts);
+
+ result = isc_ht_find(target->entries, key,
+ (uint32_t)keysize, (void **) &oentry);
+ if (result != ISC_R_SUCCESS) {
+ result = isc_ht_add(toadd, key, (uint32_t)keysize,
+ nentry);
+ if (result != ISC_R_SUCCESS)
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_MASTER,
+ ISC_LOG_ERROR,
+ "catz: error adding zone '%s' "
+ "from catalog '%s' - %s",
+ zname, czname,
+ isc_result_totext(result));
+ continue;
+ }
+
+ if (dns_catz_entry_cmp(oentry, nentry) != true) {
+ result = isc_ht_add(tomod, key, (uint32_t)keysize,
+ nentry);
+ if (result != ISC_R_SUCCESS)
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_MASTER,
+ ISC_LOG_ERROR,
+ "catz: error modifying zone '%s' "
+ "from catalog '%s' - %s",
+ zname, czname,
+ isc_result_totext(result));
+ }
+ dns_catz_entry_detach(target, &oentry);
+ result = isc_ht_delete(target->entries, key,
+ (uint32_t)keysize);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ }
+ RUNTIME_CHECK(result == ISC_R_NOMORE);
+ isc_ht_iter_destroy(&iter1);
+
+ /*
+ * Then - walk the old zone; only deleted entries should remain.
+ */
+ for (result = isc_ht_iter_first(iter2);
+ result == ISC_R_SUCCESS;
+ result = isc_ht_iter_delcurrent_next(iter2))
+ {
+ dns_catz_entry_t *entry;
+ isc_ht_iter_current(iter2, (void **) &entry);
+
+ dns_name_format(&entry->name, zname, DNS_NAME_FORMATSIZE);
+ result = delzone(entry, target, target->catzs->view,
+ target->catzs->taskmgr,
+ target->catzs->zmm->udata);
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_MASTER, ISC_LOG_INFO,
+ "catz: deleting zone '%s' from catalog '%s' - %s",
+ zname, czname, isc_result_totext(result));
+ dns_catz_entry_detach(target, &entry);
+ }
+ RUNTIME_CHECK(result == ISC_R_NOMORE);
+ isc_ht_iter_destroy(&iter2);
+ /* At this moment target->entries has to be be empty. */
+ INSIST(isc_ht_count(target->entries) == 0);
+ isc_ht_destroy(&target->entries);
+
+ for (result = isc_ht_iter_first(iteradd);
+ result == ISC_R_SUCCESS;
+ result = isc_ht_iter_delcurrent_next(iteradd))
+ {
+ dns_catz_entry_t *entry;
+ isc_ht_iter_current(iteradd, (void **) &entry);
+
+ dns_name_format(&entry->name, zname, DNS_NAME_FORMATSIZE);
+ result = addzone(entry, target, target->catzs->view,
+ target->catzs->taskmgr,
+ target->catzs->zmm->udata);
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_MASTER, ISC_LOG_INFO,
+ "catz: adding zone '%s' from catalog "
+ "'%s' - %s",
+ zname, czname,
+ isc_result_totext(result));
+ }
+
+ for (result = isc_ht_iter_first(itermod);
+ result == ISC_R_SUCCESS;
+ result = isc_ht_iter_delcurrent_next(itermod))
+ {
+ dns_catz_entry_t *entry;
+ isc_ht_iter_current(itermod, (void **) &entry);
+ result = modzone(entry, target, target->catzs->view,
+ target->catzs->taskmgr,
+ target->catzs->zmm->udata);
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_MASTER, ISC_LOG_INFO,
+ "catz: modifying zone '%s' from catalog "
+ "'%s' - %s",
+ zname, czname,
+ isc_result_totext(result));
+ }
+
+ target->entries = newzone->entries;
+ newzone->entries = NULL;
+
+ result = ISC_R_SUCCESS;
+
+cleanup:
+ if (iter1 != NULL)
+ isc_ht_iter_destroy(&iter1);
+ if (iter2 != NULL)
+ isc_ht_iter_destroy(&iter2);
+ if (iteradd != NULL)
+ isc_ht_iter_destroy(&iteradd);
+ if (itermod != NULL)
+ isc_ht_iter_destroy(&itermod);
+ if (toadd != NULL)
+ isc_ht_destroy(&toadd);
+ if (tomod != NULL)
+ isc_ht_destroy(&tomod);
+ return (result);
+
+}
+
+isc_result_t
+dns_catz_new_zones(dns_catz_zones_t **catzsp, dns_catz_zonemodmethods_t *zmm,
+ isc_mem_t *mctx, isc_taskmgr_t *taskmgr,
+ isc_timermgr_t *timermgr)
+{
+ dns_catz_zones_t *new_zones;
+ isc_result_t result;
+
+ REQUIRE(catzsp != NULL && *catzsp == NULL);
+ REQUIRE(zmm != NULL);
+
+ new_zones = isc_mem_get(mctx, sizeof(*new_zones));
+ if (new_zones == NULL)
+ return (ISC_R_NOMEMORY);
+ memset(new_zones, 0, sizeof(*new_zones));
+
+ result = isc_mutex_init(&new_zones->lock);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_newzones;
+
+ result = isc_refcount_init(&new_zones->refs, 1);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_mutex;
+
+ result = isc_ht_init(&new_zones->zones, mctx, 4);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_refcount;
+
+ isc_mem_attach(mctx, &new_zones->mctx);
+ new_zones->zmm = zmm;
+ new_zones->timermgr = timermgr;
+ new_zones->taskmgr = taskmgr;
+
+ result = isc_task_create(taskmgr, 0, &new_zones->updater);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_ht;
+
+ *catzsp = new_zones;
+ return (ISC_R_SUCCESS);
+
+ cleanup_ht:
+ isc_ht_destroy(&new_zones->zones);
+ cleanup_refcount:
+ isc_refcount_destroy(&new_zones->refs);
+ cleanup_mutex:
+ isc_mutex_destroy(&new_zones->lock);
+ cleanup_newzones:
+ isc_mem_put(mctx, new_zones, sizeof(*new_zones));
+
+ return (result);
+}
+
+void
+dns_catz_catzs_set_view(dns_catz_zones_t *catzs, dns_view_t *view) {
+ REQUIRE(catzs != NULL);
+ REQUIRE(view != NULL);
+ /* Either it's a new one or it's being reconfigured. */
+ REQUIRE(catzs->view == NULL || !strcmp(catzs->view->name, view->name));
+
+ catzs->view = view;
+}
+
+isc_result_t
+dns_catz_new_zone(dns_catz_zones_t *catzs, dns_catz_zone_t **zonep,
+ const dns_name_t *name)
+{
+ isc_result_t result;
+ dns_catz_zone_t *new_zone;
+
+ REQUIRE(zonep != NULL && *zonep == NULL);
+
+ new_zone = isc_mem_get(catzs->mctx, sizeof(*new_zone));
+ if (new_zone == NULL)
+ return (ISC_R_NOMEMORY);
+
+ memset(new_zone, 0, sizeof(*new_zone));
+
+ dns_name_init(&new_zone->name, NULL);
+
+ result = dns_name_dup(name, catzs->mctx, &new_zone->name);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_newzone;
+
+ result = isc_ht_init(&new_zone->entries, catzs->mctx, 4);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_name;
+
+ new_zone->updatetimer = NULL;
+ result = isc_timer_create(catzs->timermgr, isc_timertype_inactive,
+ NULL, NULL, catzs->updater,
+ dns_catz_update_taskaction,
+ new_zone, &new_zone->updatetimer);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_ht;
+
+ isc_time_settoepoch(&new_zone->lastupdated);
+ new_zone->updatepending = false;
+ new_zone->db = NULL;
+ new_zone->dbversion = NULL;
+ new_zone->catzs = catzs;
+ dns_catz_options_init(&new_zone->defoptions);
+ dns_catz_options_init(&new_zone->zoneoptions);
+ new_zone->active = true;
+ new_zone->db_registered = false;
+ new_zone->version = (uint32_t)(-1);
+ isc_refcount_init(&new_zone->refs, 1);
+
+ *zonep = new_zone;
+
+ return (ISC_R_SUCCESS);
+
+ cleanup_ht:
+ isc_ht_destroy(&new_zone->entries);
+ cleanup_name:
+ dns_name_free(&new_zone->name, catzs->mctx);
+ cleanup_newzone:
+ isc_mem_put(catzs->mctx, new_zone, sizeof(*new_zone));
+
+ return (result);
+}
+
+isc_result_t
+dns_catz_add_zone(dns_catz_zones_t *catzs, const dns_name_t *name,
+ dns_catz_zone_t **zonep)
+{
+ dns_catz_zone_t *new_zone = NULL;
+ isc_result_t result, tresult;
+ char zname[DNS_NAME_FORMATSIZE];
+
+ REQUIRE(catzs != NULL);
+ REQUIRE(name != NULL);
+ REQUIRE(zonep != NULL && *zonep == NULL);
+ dns_name_format(name, zname, DNS_NAME_FORMATSIZE);
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_MASTER, ISC_LOG_DEBUG(3),
+ "catz: dns_catz_add_zone %s", zname);
+
+ LOCK(&catzs->lock);
+
+ result = dns_catz_new_zone(catzs, &new_zone, name);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = isc_ht_add(catzs->zones, new_zone->name.ndata,
+ new_zone->name.length, new_zone);
+ if (result != ISC_R_SUCCESS) {
+ dns_catz_zone_detach(&new_zone);
+ if (result != ISC_R_EXISTS)
+ goto cleanup;
+ }
+
+ if (result == ISC_R_EXISTS) {
+ tresult = isc_ht_find(catzs->zones, name->ndata,
+ name->length, (void **) &new_zone);
+ INSIST(tresult == ISC_R_SUCCESS && !new_zone->active);
+ new_zone->active = true;
+ }
+
+ *zonep = new_zone;
+
+ cleanup:
+ UNLOCK(&catzs->lock);
+
+ return (result);
+}
+
+dns_catz_zone_t *
+dns_catz_get_zone(dns_catz_zones_t *catzs, const dns_name_t *name) {
+ isc_result_t result;
+ dns_catz_zone_t *found;
+
+ result = isc_ht_find(catzs->zones, name->ndata, name->length,
+ (void **) &found);
+ if (result != ISC_R_SUCCESS)
+ return (NULL);
+
+ return (found);
+}
+
+void
+dns_catz_catzs_attach(dns_catz_zones_t *catzs, dns_catz_zones_t **catzsp) {
+ REQUIRE(catzsp != NULL && *catzsp == NULL);
+
+ isc_refcount_increment(&catzs->refs, NULL);
+ *catzsp = catzs;
+}
+
+void
+dns_catz_zone_attach(dns_catz_zone_t *zone, dns_catz_zone_t **zonep) {
+ REQUIRE(zonep != NULL && *zonep == NULL);
+
+ isc_refcount_increment(&zone->refs, NULL);
+ *zonep = zone;
+}
+
+void
+dns_catz_zone_detach(dns_catz_zone_t **zonep) {
+ isc_result_t result;
+ dns_catz_zone_t *zone;
+ isc_ht_iter_t *iter = NULL;
+ isc_mem_t *mctx;
+ unsigned int refs;
+
+ REQUIRE(zonep != NULL && *zonep != NULL);
+
+ zone = *zonep;
+ *zonep = NULL;
+ isc_refcount_decrement(&zone->refs, &refs);
+ if (refs == 0) {
+ if (zone->entries != NULL) {
+ result = isc_ht_iter_create(zone->entries, &iter);
+ INSIST(result == ISC_R_SUCCESS);
+ for (result = isc_ht_iter_first(iter);
+ result == ISC_R_SUCCESS;
+ result = isc_ht_iter_delcurrent_next(iter))
+ {
+ dns_catz_entry_t *entry;
+
+ isc_ht_iter_current(iter, (void **) &entry);
+ dns_catz_entry_detach(zone, &entry);
+ }
+ INSIST(result == ISC_R_NOMORE);
+ isc_ht_iter_destroy(&iter);
+
+ /* The hashtable has to be empty now. */
+ INSIST(isc_ht_count(zone->entries) == 0);
+ isc_ht_destroy(&zone->entries);
+ }
+ mctx = zone->catzs->mctx;
+ isc_timer_detach(&zone->updatetimer);
+ isc_refcount_destroy(&zone->refs);
+ if (zone->db_registered == true) {
+ result = dns_db_updatenotify_unregister(zone->db,
+ dns_catz_dbupdate_callback,
+ zone->catzs);
+ INSIST(result == ISC_R_SUCCESS);
+ }
+ if (zone->dbversion)
+ dns_db_closeversion(zone->db, &zone->dbversion,
+ false);
+ if (zone->db != NULL)
+ dns_db_detach(&zone->db);
+
+ dns_name_free(&zone->name, mctx);
+ dns_catz_options_free(&zone->defoptions, mctx);
+ dns_catz_options_free(&zone->zoneoptions, mctx);
+
+ zone->catzs = NULL;
+ isc_mem_put(mctx, zone, sizeof(dns_catz_zone_t));
+ }
+}
+
+void
+dns_catz_catzs_detach(dns_catz_zones_t ** catzsp) {
+ dns_catz_zones_t *catzs;
+ isc_ht_iter_t *iter = NULL;
+ isc_result_t result;
+ unsigned int refs;
+ dns_catz_zone_t *zone;
+
+
+ REQUIRE(catzsp != NULL);
+ catzs = *catzsp;
+ REQUIRE(catzs != NULL);
+
+ *catzsp = NULL;
+ isc_refcount_decrement(&catzs->refs, &refs);
+
+ if (refs == 0) {
+ DESTROYLOCK(&catzs->lock);
+ if (catzs->zones != NULL) {
+ result = isc_ht_iter_create(catzs->zones, &iter);
+ INSIST(result == ISC_R_SUCCESS);
+ for (result = isc_ht_iter_first(iter);
+ result == ISC_R_SUCCESS;)
+ {
+ isc_ht_iter_current(iter, (void **) &zone);
+ result = isc_ht_iter_delcurrent_next(iter);
+ dns_catz_zone_detach(&zone);
+ }
+ INSIST(result == ISC_R_NOMORE);
+ isc_ht_iter_destroy(&iter);
+ INSIST(isc_ht_count(catzs->zones) == 0);
+ isc_ht_destroy(&catzs->zones);
+ }
+ isc_refcount_destroy(&catzs->refs);
+ isc_task_destroy(&catzs->updater);
+ isc_mem_putanddetach(&catzs->mctx, catzs, sizeof(*catzs));
+ }
+}
+
+typedef enum {
+ CATZ_OPT_NONE,
+ CATZ_OPT_ZONES,
+ CATZ_OPT_MASTERS,
+ CATZ_OPT_ALLOW_QUERY,
+ CATZ_OPT_ALLOW_TRANSFER,
+ CATZ_OPT_VERSION,
+} catz_opt_t;
+
+static bool
+catz_opt_cmp(const dns_label_t *option, const char *opt) {
+ unsigned int l = strlen(opt);
+ if (option->length - 1 == l &&
+ memcmp(opt, option->base + 1, l - 1) == 0)
+ return (true);
+ else
+ return (false);
+}
+
+static catz_opt_t
+catz_get_option(const dns_label_t *option) {
+ if (catz_opt_cmp(option, "zones"))
+ return (CATZ_OPT_ZONES);
+ else if (catz_opt_cmp(option, "masters"))
+ return (CATZ_OPT_MASTERS);
+ else if (catz_opt_cmp(option, "allow-query"))
+ return (CATZ_OPT_ALLOW_QUERY);
+ else if (catz_opt_cmp(option, "allow-transfer"))
+ return (CATZ_OPT_ALLOW_TRANSFER);
+ else if (catz_opt_cmp(option, "version"))
+ return (CATZ_OPT_VERSION);
+ else
+ return (CATZ_OPT_NONE);
+}
+
+static isc_result_t
+catz_process_zones(dns_catz_zone_t *zone, dns_rdataset_t *value,
+ dns_name_t *name)
+{
+ dns_label_t mhash;
+ dns_name_t opt;
+
+ REQUIRE(zone != NULL);
+ REQUIRE(DNS_RDATASET_VALID(value));
+ REQUIRE(name != NULL);
+
+ if (value->rdclass != dns_rdataclass_in)
+ return (ISC_R_FAILURE);
+
+ if (name->labels == 0)
+ return (ISC_R_FAILURE);
+
+ dns_name_getlabel(name, name->labels-1, &mhash);
+
+ if (name->labels == 1)
+ return (catz_process_zones_entry(zone, value, &mhash));
+ else {
+ dns_name_init(&opt, NULL);
+ dns_name_split(name, 1, &opt, NULL);
+ return (catz_process_zones_suboption(zone, value, &mhash, &opt));
+ }
+}
+
+static isc_result_t
+catz_process_zones_entry(dns_catz_zone_t *zone, dns_rdataset_t *value,
+ dns_label_t *mhash)
+{
+ isc_result_t result;
+ dns_rdata_t rdata;
+ dns_rdata_ptr_t ptr;
+ dns_catz_entry_t *entry = NULL;
+
+ /*
+ * We only take -first- value, as mhash must be
+ * different.
+ */
+ if (value->type != dns_rdatatype_ptr)
+ return (ISC_R_FAILURE);
+
+ result = dns_rdataset_first(value);
+ if (result != ISC_R_SUCCESS)
+ return (ISC_R_FAILURE);
+
+ dns_rdata_init(&rdata);
+ dns_rdataset_current(value, &rdata);
+
+ result = dns_rdata_tostruct(&rdata, &ptr, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ result = isc_ht_find(zone->entries, mhash->base,
+ mhash->length, (void **) &entry);
+ if (result == ISC_R_SUCCESS) {
+ if (dns_name_countlabels(&entry->name) != 0) {
+ /* We have a duplicate. */
+ dns_rdata_freestruct(&ptr);
+ return (ISC_R_FAILURE);
+ } else {
+ result = dns_name_dup(&ptr.ptr, zone->catzs->mctx,
+ &entry->name);
+ if (result != ISC_R_SUCCESS) {
+ dns_rdata_freestruct(&ptr);
+ return (result);
+ }
+ }
+ } else {
+ result = dns_catz_entry_new(zone->catzs->mctx, &ptr.ptr,
+ &entry);
+ if (result != ISC_R_SUCCESS) {
+ dns_rdata_freestruct(&ptr);
+ return (result);
+ }
+
+ result = isc_ht_add(zone->entries, mhash->base,
+ mhash->length, entry);
+ if (result != ISC_R_SUCCESS) {
+ dns_rdata_freestruct(&ptr);
+ dns_catz_entry_detach(zone, &entry);
+ return (result);
+ }
+ }
+
+ dns_rdata_freestruct(&ptr);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+catz_process_version(dns_catz_zone_t *zone, dns_rdataset_t *value) {
+ isc_result_t result;
+ dns_rdata_t rdata;
+ dns_rdata_txt_t rdatatxt;
+ dns_rdata_txt_string_t rdatastr;
+ uint32_t tversion;
+ char t[16];
+
+ REQUIRE(zone != NULL);
+ REQUIRE(DNS_RDATASET_VALID(value));
+
+ if (value->rdclass != dns_rdataclass_in ||
+ value->type != dns_rdatatype_txt)
+ return (ISC_R_FAILURE);
+
+ result = dns_rdataset_first(value);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ dns_rdata_init(&rdata);
+ dns_rdataset_current(value, &rdata);
+
+ result = dns_rdata_tostruct(&rdata, &rdatatxt, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ result = dns_rdata_txt_first(&rdatatxt);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = dns_rdata_txt_current(&rdatatxt, &rdatastr);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = dns_rdata_txt_next(&rdatatxt);
+ if (result != ISC_R_NOMORE) {
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+ if (rdatastr.length > 15) {
+ result = ISC_R_BADNUMBER;
+ goto cleanup;
+ }
+ memmove(t, rdatastr.data, rdatastr.length);
+ t[rdatastr.length] = 0;
+ result = isc_parse_uint32(&tversion, t, 10);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+ zone->version = tversion;
+ result = ISC_R_SUCCESS;
+
+cleanup:
+ dns_rdata_freestruct(&rdatatxt);
+ return (result);
+}
+
+static isc_result_t
+catz_process_masters(dns_catz_zone_t *zone, dns_ipkeylist_t *ipkl,
+ dns_rdataset_t *value, dns_name_t *name)
+{
+ isc_result_t result;
+ dns_rdata_t rdata;
+ dns_rdata_in_a_t rdata_a;
+ dns_rdata_in_aaaa_t rdata_aaaa;
+ dns_rdata_txt_t rdata_txt;
+ dns_rdata_txt_string_t rdatastr;
+ dns_name_t *keyname = NULL;
+ isc_mem_t *mctx;
+ char keycbuf[DNS_NAME_FORMATSIZE];
+ isc_buffer_t keybuf;
+ unsigned int rcount;
+ unsigned int i;
+
+ REQUIRE(zone != NULL);
+ REQUIRE(ipkl != NULL);
+ REQUIRE(DNS_RDATASET_VALID(value));
+ REQUIRE(dns_rdataset_isassociated(value));
+ REQUIRE(name != NULL);
+
+ mctx = zone->catzs->mctx;
+ memset(&rdata_a, 0, sizeof(rdata_a));
+ memset(&rdata_aaaa, 0, sizeof(rdata_aaaa));
+ memset(&rdata_txt, 0, sizeof(rdata_txt));
+ isc_buffer_init(&keybuf, keycbuf, sizeof(keycbuf));
+
+ /*
+ * We have three possibilities here:
+ * - either empty name and IN A/IN AAAA record
+ * - label and IN A/IN AAAA
+ * - label and IN TXT - TSIG key name
+ */
+ if (value->rdclass != dns_rdataclass_in)
+ return (ISC_R_FAILURE);
+
+ if (name->labels > 0) {
+ isc_sockaddr_t sockaddr;
+
+ /*
+ * We're pre-preparing the data once, we'll put it into
+ * the right spot in the masters array once we find it.
+ */
+ result = dns_rdataset_first(value);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ dns_rdata_init(&rdata);
+ dns_rdataset_current(value, &rdata);
+ switch (value->type) {
+ case dns_rdatatype_a:
+ result = dns_rdata_tostruct(&rdata, &rdata_a, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ isc_sockaddr_fromin(&sockaddr, &rdata_a.in_addr, 0);
+ break;
+ case dns_rdatatype_aaaa:
+ result = dns_rdata_tostruct(&rdata, &rdata_aaaa, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ isc_sockaddr_fromin6(&sockaddr, &rdata_aaaa.in6_addr, 0);
+ break;
+ case dns_rdatatype_txt:
+ result = dns_rdata_tostruct(&rdata, &rdata_txt, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ result = dns_rdata_txt_first(&rdata_txt);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = dns_rdata_txt_current(&rdata_txt, &rdatastr);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = dns_rdata_txt_next(&rdata_txt);
+ if (result != ISC_R_NOMORE)
+ return (ISC_R_FAILURE);
+
+ /* rdatastr.length < DNS_NAME_MAXTEXT */
+ keyname = isc_mem_get(mctx, sizeof(dns_name_t));
+ if (keyname == NULL)
+ return (ISC_R_NOMEMORY);
+ dns_name_init(keyname, 0);
+ memmove(keycbuf, rdatastr.data, rdatastr.length);
+ keycbuf[rdatastr.length] = 0;
+ result = dns_name_fromstring(keyname, keycbuf, 0, mctx);
+ if (result != ISC_R_SUCCESS) {
+ dns_name_free(keyname, mctx);
+ isc_mem_put(mctx, keyname, sizeof(dns_name_t));
+ return (result);
+ }
+ break;
+ default:
+ return (ISC_R_FAILURE);
+ }
+
+ /*
+ * We have to find the appropriate labeled record in masters
+ * if it exists.
+ * In common case we'll have no more than 3-4 records here so
+ * no optimization.
+ */
+ for (i = 0; i < ipkl->count; i++) {
+ if (ipkl->labels[i] != NULL &&
+ !dns_name_compare(name, ipkl->labels[i]))
+ break;
+ }
+
+ if (i < ipkl->count) { /* we have this record already */
+ if (value->type == dns_rdatatype_txt)
+ ipkl->keys[i] = keyname;
+ else /* A/AAAA */
+ memmove(&ipkl->addrs[i], &sockaddr,
+ sizeof(isc_sockaddr_t));
+ } else {
+ result = dns_ipkeylist_resize(mctx, ipkl,
+ i+1);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ ipkl->labels[i] = isc_mem_get(mctx, sizeof(dns_name_t));
+ if (ipkl->labels[i] == NULL) {
+ if (keyname != NULL) {
+ dns_name_free(keyname, mctx);
+ isc_mem_put(mctx, keyname,
+ sizeof(dns_name_t));
+ }
+ return (ISC_R_NOMEMORY);
+ }
+ dns_name_init(ipkl->labels[i], NULL);
+ result = dns_name_dup(name, mctx, ipkl->labels[i]);
+ if (result != ISC_R_SUCCESS) {
+ if (keyname != NULL) {
+ dns_name_free(keyname, mctx);
+ isc_mem_put(mctx, keyname,
+ sizeof(dns_name_t));
+ }
+ return (result);
+ }
+
+ if (value->type == dns_rdatatype_txt)
+ ipkl->keys[i] = keyname;
+ else /* A/AAAA */
+ memmove(&ipkl->addrs[i], &sockaddr,
+ sizeof(isc_sockaddr_t));
+ ipkl->count++;
+ }
+ return (ISC_R_SUCCESS);
+ }
+ /* else - 'simple' case - without labels */
+
+ if (value->type != dns_rdatatype_a &&
+ value->type != dns_rdatatype_aaaa)
+ return (ISC_R_FAILURE);
+
+ rcount = dns_rdataset_count(value) + ipkl->count;
+
+ result = dns_ipkeylist_resize(mctx, ipkl, rcount);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ for (result = dns_rdataset_first(value);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(value))
+ {
+ dns_rdata_init(&rdata);
+ dns_rdataset_current(value, &rdata);
+ /*
+ * port 0 == take the default
+ */
+ if (value->type == dns_rdatatype_a) {
+ result = dns_rdata_tostruct(&rdata, &rdata_a, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ isc_sockaddr_fromin(&ipkl->addrs[ipkl->count],
+ &rdata_a.in_addr, 0);
+ } else {
+ result = dns_rdata_tostruct(&rdata, &rdata_aaaa, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ isc_sockaddr_fromin6(&ipkl->addrs[ipkl->count],
+ &rdata_aaaa.in6_addr, 0);
+ }
+ ipkl->keys[ipkl->count] = NULL;
+ ipkl->labels[ipkl->count] = NULL;
+ ipkl->count++;
+ dns_rdata_freestruct(&rdata_a);
+ }
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+catz_process_apl(dns_catz_zone_t *zone, isc_buffer_t **aclbp,
+ dns_rdataset_t *value)
+{
+ isc_result_t result = ISC_R_SUCCESS;
+ dns_rdata_t rdata;
+ dns_rdata_in_apl_t rdata_apl;
+ dns_rdata_apl_ent_t apl_ent;
+ isc_netaddr_t addr;
+ isc_buffer_t *aclb = NULL;
+ unsigned char buf[256]; /* larger than INET6_ADDRSTRLEN */
+
+ REQUIRE(zone != NULL);
+ REQUIRE(aclbp != NULL);
+ REQUIRE(*aclbp == NULL);
+ REQUIRE(DNS_RDATASET_VALID(value));
+ REQUIRE(dns_rdataset_isassociated(value));
+
+ if (value->rdclass != dns_rdataclass_in ||
+ value->type != dns_rdatatype_apl)
+ return (ISC_R_FAILURE);
+
+
+ if (dns_rdataset_count(value) > 1) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_MASTER, ISC_LOG_WARNING,
+ "catz: more than one APL entry for member zone, "
+ "result is undefined");
+ }
+ result = dns_rdataset_first(value);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ dns_rdata_init(&rdata);
+ dns_rdataset_current(value, &rdata);
+ result = dns_rdata_tostruct(&rdata, &rdata_apl, zone->catzs->mctx);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ result = isc_buffer_allocate(zone->catzs->mctx, &aclb, 16);
+ isc_buffer_setautorealloc(aclb, true);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ for (result = dns_rdata_apl_first(&rdata_apl);
+ result == ISC_R_SUCCESS;
+ result = dns_rdata_apl_next(&rdata_apl)) {
+ result = dns_rdata_apl_current(&rdata_apl, &apl_ent);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ memset(buf, 0, sizeof(buf));
+ if (apl_ent.data != NULL && apl_ent.length > 0)
+ memmove(buf, apl_ent.data, apl_ent.length);
+ if (apl_ent.family == 1)
+ isc_netaddr_fromin(&addr, (struct in_addr*) buf);
+ else if (apl_ent.family == 2)
+ isc_netaddr_fromin6(&addr, (struct in6_addr*) buf);
+ else
+ continue; /* xxxwpk log it or simply ignore? */
+ if (apl_ent.negative)
+ isc_buffer_putuint8(aclb, '!');
+ isc_buffer_reserve(&aclb, INET6_ADDRSTRLEN);
+ result = isc_netaddr_totext(&addr, aclb);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ if ((apl_ent.family == 1 && apl_ent.prefix < 32) ||
+ (apl_ent.family == 2 && apl_ent.prefix < 128)) {
+ isc_buffer_putuint8(aclb, '/');
+ isc_buffer_putdecint(aclb, apl_ent.prefix);
+ }
+ isc_buffer_putstr(aclb, "; ");
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ else
+ goto cleanup;
+ *aclbp = aclb;
+ aclb = NULL;
+cleanup:
+ if (aclb != NULL)
+ isc_buffer_free(&aclb);
+ dns_rdata_freestruct(&rdata_apl);
+ return (result);
+}
+
+static isc_result_t
+catz_process_zones_suboption(dns_catz_zone_t *zone, dns_rdataset_t *value,
+ dns_label_t *mhash, dns_name_t *name)
+{
+ isc_result_t result;
+ dns_catz_entry_t *entry = NULL;
+ dns_label_t option;
+ dns_name_t prefix;
+ catz_opt_t opt;
+
+ REQUIRE(zone != NULL);
+ REQUIRE(mhash != NULL);
+ REQUIRE(DNS_RDATASET_VALID(value));
+
+ if (name->labels == 0)
+ return (ISC_R_FAILURE);
+ dns_name_getlabel(name, name->labels - 1, &option);
+ opt = catz_get_option(&option);
+
+ /*
+ * We're adding this entry now, in case the option is invalid we'll get
+ * rid of it in verification phase.
+ */
+ result = isc_ht_find(zone->entries, mhash->base, mhash->length,
+ (void **) &entry);
+ if (result != ISC_R_SUCCESS) {
+ result = dns_catz_entry_new(zone->catzs->mctx, NULL, &entry);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ result = isc_ht_add(zone->entries, mhash->base, mhash->length,
+ entry);
+ if (result != ISC_R_SUCCESS) {
+ dns_catz_entry_detach(zone, &entry);
+ return (result);
+ }
+ }
+
+ dns_name_init(&prefix, NULL);
+ dns_name_split(name, 1, &prefix, NULL);
+ switch (opt) {
+ case CATZ_OPT_MASTERS:
+ return (catz_process_masters(zone, &entry->opts.masters, value,
+ &prefix));
+ case CATZ_OPT_ALLOW_QUERY:
+ if (prefix.labels != 0)
+ return (ISC_R_FAILURE);
+ return (catz_process_apl(zone, &entry->opts.allow_query,
+ value));
+ case CATZ_OPT_ALLOW_TRANSFER:
+ if (prefix.labels != 0)
+ return (ISC_R_FAILURE);
+ return (catz_process_apl(zone, &entry->opts.allow_transfer,
+ value));
+ default:
+ return (ISC_R_FAILURE);
+ }
+
+ return (ISC_R_FAILURE);
+}
+
+static isc_result_t
+catz_process_value(dns_catz_zone_t *zone, dns_name_t *name,
+ dns_rdataset_t *rdataset)
+{
+ dns_label_t option;
+ dns_name_t prefix;
+ catz_opt_t opt;
+
+ REQUIRE(zone != NULL);
+ REQUIRE(name != NULL);
+ REQUIRE(DNS_RDATASET_VALID(rdataset));
+
+ dns_name_getlabel(name, name->labels - 1, &option);
+ opt = catz_get_option(&option);
+ dns_name_init(&prefix, NULL);
+ dns_name_split(name, 1, &prefix, NULL);
+ switch (opt) {
+ case CATZ_OPT_ZONES:
+ return (catz_process_zones(zone, rdataset, &prefix));
+ case CATZ_OPT_MASTERS:
+ return (catz_process_masters(zone, &zone->zoneoptions.masters,
+ rdataset, &prefix));
+ case CATZ_OPT_ALLOW_QUERY:
+ if (prefix.labels != 0)
+ return (ISC_R_FAILURE);
+ return (catz_process_apl(zone, &zone->zoneoptions.allow_query,
+ rdataset));
+ case CATZ_OPT_ALLOW_TRANSFER:
+ if (prefix.labels != 0)
+ return (ISC_R_FAILURE);
+ return (catz_process_apl(zone,
+ &zone->zoneoptions.allow_transfer,
+ rdataset));
+ case CATZ_OPT_VERSION:
+ if (prefix.labels != 0)
+ return (ISC_R_FAILURE);
+ return (catz_process_version(zone, rdataset));
+ default:
+ return (ISC_R_FAILURE);
+ }
+}
+
+isc_result_t
+dns_catz_update_process(dns_catz_zones_t *catzs, dns_catz_zone_t *zone,
+ dns_name_t *src_name, dns_rdataset_t *rdataset)
+{
+ isc_result_t result;
+ int order;
+ unsigned int nlabels;
+ dns_namereln_t nrres;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_soa_t soa;
+ dns_name_t prefix;
+
+ REQUIRE(catzs != NULL);
+ REQUIRE(zone != NULL);
+
+ nrres = dns_name_fullcompare(src_name, &zone->name, &order, &nlabels);
+ if (nrres == dns_namereln_equal) {
+ if (rdataset->type == dns_rdatatype_soa) {
+ result = dns_rdataset_first(rdataset);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ dns_rdataset_current(rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &soa, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ /*
+ * xxxwpk TODO do we want to save something from SOA?
+ */
+ return (result);
+
+ } else if (rdataset->type == dns_rdatatype_ns) {
+ return (ISC_R_SUCCESS);
+ } else {
+ return (ISC_R_UNEXPECTED);
+ }
+ } else if (nrres != dns_namereln_subdomain) {
+ return (ISC_R_UNEXPECTED);
+ }
+
+ dns_name_init(&prefix, NULL);
+ dns_name_split(src_name, zone->name.labels, &prefix, NULL);
+ result = catz_process_value(zone, &prefix, rdataset);
+
+ return (result);
+}
+
+isc_result_t
+dns_catz_generate_masterfilename(dns_catz_zone_t *zone, dns_catz_entry_t *entry,
+ isc_buffer_t **buffer)
+{
+ isc_buffer_t *tbuf = NULL;
+ isc_sha256_t sha256;
+ isc_region_t r;
+ isc_result_t result;
+ size_t rlen;
+
+ REQUIRE(zone != NULL);
+ REQUIRE(entry != NULL);
+ REQUIRE(buffer != NULL && *buffer != NULL);
+
+ result = isc_buffer_allocate(zone->catzs->mctx, &tbuf,
+ strlen(zone->catzs->view->name) +
+ 2 * DNS_NAME_FORMATSIZE + 2);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ INSIST(tbuf != NULL);
+
+ isc_buffer_putstr(tbuf, zone->catzs->view->name);
+ isc_buffer_putstr(tbuf, "_");
+ result = dns_name_totext(&zone->name, true, tbuf);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ isc_buffer_putstr(tbuf, "_");
+ result = dns_name_totext(&entry->name, true, tbuf);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ /* __catz__<digest>.db */
+ rlen = ISC_SHA256_DIGESTSTRINGLENGTH + 12;
+
+ /* optionally prepend with <zonedir>/ */
+ if (entry->opts.zonedir != NULL)
+ rlen += strlen(entry->opts.zonedir) + 1;
+
+ result = isc_buffer_reserve(buffer, (unsigned int)rlen);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ if (entry->opts.zonedir != NULL) {
+ isc_buffer_putstr(*buffer, entry->opts.zonedir);
+ isc_buffer_putstr(*buffer, "/");
+ }
+
+ isc_buffer_usedregion(tbuf, &r);
+ isc_buffer_putstr(*buffer, "__catz__");
+ if (tbuf->used > ISC_SHA256_DIGESTSTRINGLENGTH) {
+ isc_sha256_init(&sha256);
+ isc_sha256_update(&sha256, r.base, r.length);
+ /* we can do that because digest string < 2 * DNS_NAME */
+ isc_sha256_end(&sha256, (char *) r.base);
+ isc_buffer_putstr(*buffer, (char *) r.base);
+ } else {
+ isc_buffer_copyregion(*buffer, &r);
+ }
+
+ isc_buffer_putstr(*buffer, ".db");
+ result = ISC_R_SUCCESS;
+
+cleanup:
+ isc_buffer_free(&tbuf);
+ return (result);
+}
+
+isc_result_t
+dns_catz_generate_zonecfg(dns_catz_zone_t *zone, dns_catz_entry_t *entry,
+ isc_buffer_t **buf)
+{
+ /*
+ * We have to generate a text buffer with regular zone config:
+ * zone foo.bar {
+ * type slave;
+ * masters [ dscp X ] { ip1 port port1; ip2 port port2; };
+ * }
+ */
+ isc_buffer_t *buffer = NULL;
+ isc_region_t region;
+ isc_result_t result;
+ uint32_t i;
+ isc_netaddr_t netaddr;
+ char pbuf[sizeof("65535")]; /* used both for port number and DSCP */
+ char zname[DNS_NAME_FORMATSIZE];
+
+ REQUIRE(zone != NULL);
+ REQUIRE(entry != NULL);
+ REQUIRE(buf != NULL && *buf == NULL);
+
+ /*
+ * The buffer will be reallocated if something won't fit,
+ * ISC_BUFFER_INCR seems like a good start.
+ */
+ result = isc_buffer_allocate(zone->catzs->mctx, &buffer,
+ ISC_BUFFER_INCR);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ isc_buffer_setautorealloc(buffer, true);
+ isc_buffer_putstr(buffer, "zone ");
+ dns_name_totext(&entry->name, true, buffer);
+ isc_buffer_putstr(buffer, " { type slave; masters");
+
+ /*
+ * DSCP value has no default, but when it is specified, it is identical
+ * for all masters and cannot be overriden for a specific master IP, so
+ * use the DSCP value set for the first master
+ */
+ if (entry->opts.masters.count > 0 &&
+ entry->opts.masters.dscps[0] >= 0) {
+ isc_buffer_putstr(buffer, " dscp ");
+ snprintf(pbuf, sizeof(pbuf), "%hd",
+ entry->opts.masters.dscps[0]);
+ isc_buffer_putstr(buffer, pbuf);
+ }
+
+ isc_buffer_putstr(buffer, " { ");
+ for (i = 0; i < entry->opts.masters.count; i++) {
+ /*
+ * Every master must have an IP address assigned.
+ */
+ switch (entry->opts.masters.addrs[i].type.sa.sa_family) {
+ case AF_INET:
+ case AF_INET6:
+ break;
+ default:
+ dns_name_format(&entry->name, zname,
+ DNS_NAME_FORMATSIZE);
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
+ "catz: zone '%s' uses an invalid master "
+ "(no IP address assigned)",
+ zname);
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+ isc_netaddr_fromsockaddr(&netaddr,
+ &entry->opts.masters.addrs[i]);
+ isc_buffer_reserve(&buffer, INET6_ADDRSTRLEN);
+ result = isc_netaddr_totext(&netaddr, buffer);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ isc_buffer_putstr(buffer, " port ");
+ snprintf(pbuf, sizeof(pbuf), "%u",
+ isc_sockaddr_getport(&entry->opts.masters.addrs[i]));
+ isc_buffer_putstr(buffer, pbuf);
+
+ if (entry->opts.masters.keys[i] != NULL) {
+ isc_buffer_putstr(buffer, " key ");
+ result = dns_name_totext(entry->opts.masters.keys[i],
+ true, buffer);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+ isc_buffer_putstr(buffer, "; ");
+ }
+ isc_buffer_putstr(buffer, "}; ");
+ if (entry->opts.in_memory == false) {
+ isc_buffer_putstr(buffer, "file \"");
+ result = dns_catz_generate_masterfilename(zone, entry, &buffer);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ isc_buffer_putstr(buffer, "\"; ");
+
+ }
+ if (entry->opts.allow_query != NULL) {
+ isc_buffer_putstr(buffer, "allow-query { ");
+ isc_buffer_usedregion(entry->opts.allow_query, &region);
+ isc_buffer_copyregion(buffer, &region);
+ isc_buffer_putstr(buffer, "}; ");
+ }
+ if (entry->opts.allow_transfer != NULL) {
+ isc_buffer_putstr(buffer, "allow-transfer { ");
+ isc_buffer_usedregion(entry->opts.allow_transfer, &region);
+ isc_buffer_copyregion(buffer, &region);
+ isc_buffer_putstr(buffer, "}; ");
+ }
+
+ isc_buffer_putstr(buffer, "};");
+ *buf = buffer;
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ if (buffer != NULL)
+ isc_buffer_free(&buffer);
+ return (result);
+}
+
+void
+dns_catz_update_taskaction(isc_task_t *task, isc_event_t *event) {
+ isc_result_t result;
+ dns_catz_zone_t * zone;
+ (void) task;
+
+ REQUIRE(event != NULL);
+ zone = event->ev_arg;
+ REQUIRE(zone != NULL);
+
+ LOCK(&zone->catzs->lock);
+ zone->updatepending = false;
+ dns_catz_update_from_db(zone->db, zone->catzs);
+ result = isc_timer_reset(zone->updatetimer, isc_timertype_inactive,
+ NULL, NULL, true);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ isc_event_free(&event);
+ result = isc_time_now(&zone->lastupdated);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ UNLOCK(&zone->catzs->lock);
+}
+
+isc_result_t
+dns_catz_dbupdate_callback(dns_db_t *db, void *fn_arg) {
+ dns_catz_zones_t *catzs;
+ dns_catz_zone_t *zone = NULL;
+ isc_time_t now;
+ uint64_t tdiff;
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_region_t r;
+
+ REQUIRE(DNS_DB_VALID(db));
+ REQUIRE(fn_arg != NULL);
+ catzs = (dns_catz_zones_t *) fn_arg;
+
+ dns_name_toregion(&db->origin, &r);
+
+ LOCK(&catzs->lock);
+ result = isc_ht_find(catzs->zones, r.base, r.length, (void **) &zone);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ /* New zone came as AXFR */
+ if (zone->db != NULL && zone->db != db) {
+ if (zone->dbversion != NULL)
+ dns_db_closeversion(zone->db, &zone->dbversion,
+ false);
+ dns_db_detach(&zone->db);
+ /*
+ * We're not registering db update callback, it will be
+ * registered at the end of update_from_db
+ */
+ zone->db_registered = false;
+ }
+ if (zone->db == NULL)
+ dns_db_attach(db, &zone->db);
+
+ if (zone->updatepending == false) {
+ zone->updatepending = true;
+ isc_time_now(&now);
+ tdiff = isc_time_microdiff(&now, &zone->lastupdated)/1000000;
+ if (tdiff < zone->defoptions.min_update_interval) {
+ isc_interval_t interval;
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_MASTER, ISC_LOG_INFO,
+ "catz: new zone version came too soon, "
+ "deferring update");
+ isc_interval_set(&interval,
+ zone->defoptions.min_update_interval -
+ (unsigned int)tdiff, 0);
+ dns_db_currentversion(db, &zone->dbversion);
+ result = isc_timer_reset(zone->updatetimer,
+ isc_timertype_once,
+ NULL, &interval, true);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ } else {
+ isc_event_t *event;
+
+ dns_db_currentversion(db, &zone->dbversion);
+ ISC_EVENT_INIT(&zone->updateevent,
+ sizeof(zone->updateevent), 0, NULL,
+ DNS_EVENT_CATZUPDATED,
+ dns_catz_update_taskaction,
+ zone, zone, NULL, NULL);
+ event = &zone->updateevent;
+ isc_task_send(catzs->updater, &event);
+ }
+ } else {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_MASTER, ISC_LOG_DEBUG(3),
+ "catz: update already queued");
+ if (zone->dbversion != NULL)
+ dns_db_closeversion(zone->db, &zone->dbversion,
+ false);
+ dns_db_currentversion(zone->db, &zone->dbversion);
+ }
+
+ cleanup:
+ UNLOCK(&catzs->lock);
+
+ return (result);
+}
+
+void
+dns_catz_update_from_db(dns_db_t *db, dns_catz_zones_t *catzs) {
+ dns_catz_zone_t *oldzone = NULL, *newzone = NULL;
+ isc_result_t result;
+ isc_region_t r;
+ dns_dbnode_t *node = NULL;
+ dns_dbiterator_t *it = NULL;
+ dns_fixedname_t fixname;
+ dns_name_t *name;
+ dns_rdatasetiter_t *rdsiter = NULL;
+ dns_rdataset_t rdataset;
+ char bname[DNS_NAME_FORMATSIZE];
+ isc_buffer_t ibname;
+ uint32_t vers;
+
+ REQUIRE(DNS_DB_VALID(db));
+ REQUIRE(catzs != NULL);
+
+ /*
+ * Create a new catz in the same context as current catz.
+ */
+ dns_name_toregion(&db->origin, &r);
+ result = isc_ht_find(catzs->zones, r.base, r.length, (void **)&oldzone);
+ if (result != ISC_R_SUCCESS) {
+ /* This can happen if we remove the zone in the meantime. */
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
+ "catz: zone '%s' not in config",
+ bname);
+ return;
+ }
+
+ isc_buffer_init(&ibname, bname, DNS_NAME_FORMATSIZE);
+ result = dns_name_totext(&db->origin, true, &ibname);
+ INSIST(result == ISC_R_SUCCESS);
+
+ result = dns_db_getsoaserial(db, oldzone->dbversion, &vers);
+ if (result != ISC_R_SUCCESS) {
+ /* A zone without SOA record?!? */
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
+ "catz: zone '%s' has no SOA record (%s)",
+ bname, isc_result_totext(result));
+ return;
+ }
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_MASTER, ISC_LOG_INFO,
+ "catz: updating catalog zone '%s' with serial %d",
+ bname, vers);
+
+ result = dns_catz_new_zone(catzs, &newzone, &db->origin);
+ if (result != ISC_R_SUCCESS) {
+ dns_db_closeversion(db, &oldzone->dbversion, false);
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
+ "catz: failed to create new zone - %s",
+ isc_result_totext(result));
+ return;
+ }
+
+ result = dns_db_createiterator(db, DNS_DB_NONSEC3, &it);
+ if (result != ISC_R_SUCCESS) {
+ dns_catz_zone_detach(&newzone);
+ dns_db_closeversion(db, &oldzone->dbversion, false);
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
+ "catz: failed to create DB iterator - %s",
+ isc_result_totext(result));
+ return;
+ }
+
+ name = dns_fixedname_initname(&fixname);
+
+ /*
+ * Iterate over database to fill the new zone.
+ */
+ result = dns_dbiterator_first(it);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
+ "catz: failed to get db iterator - %s",
+ isc_result_totext(result));
+ }
+
+ while (result == ISC_R_SUCCESS) {
+ result = dns_dbiterator_current(it, &node, name);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
+ "catz: failed to get db iterator - %s",
+ isc_result_totext(result));
+ break;
+ }
+
+ result = dns_db_allrdatasets(db, node, oldzone->dbversion, 0,
+ &rdsiter);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
+ "catz: failed to fetch rrdatasets - %s",
+ isc_result_totext(result));
+ dns_db_detachnode(db, &node);
+ break;
+ }
+
+ dns_rdataset_init(&rdataset);
+ result = dns_rdatasetiter_first(rdsiter);
+ while (result == ISC_R_SUCCESS) {
+ dns_rdatasetiter_current(rdsiter, &rdataset);
+ result = dns_catz_update_process(catzs, newzone, name,
+ &rdataset);
+ if (result != ISC_R_SUCCESS) {
+ char cname[DNS_NAME_FORMATSIZE];
+ char typebuf[DNS_RDATATYPE_FORMATSIZE];
+ char classbuf[DNS_RDATACLASS_FORMATSIZE];
+
+ dns_name_format(name, cname,
+ DNS_NAME_FORMATSIZE);
+ dns_rdataclass_format(rdataset.rdclass,
+ classbuf,
+ sizeof(classbuf));
+ dns_rdatatype_format(rdataset.type, typebuf,
+ sizeof(typebuf));
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_MASTER,
+ ISC_LOG_WARNING,
+ "catz: unknown record in catalog "
+ "zone - %s %s %s(%s) - ignoring",
+ cname, classbuf, typebuf,
+ isc_result_totext(result));
+ }
+ dns_rdataset_disassociate(&rdataset);
+ if (result != ISC_R_SUCCESS) {
+ break;
+ }
+ result = dns_rdatasetiter_next(rdsiter);
+ }
+
+ dns_rdatasetiter_destroy(&rdsiter);
+
+ dns_db_detachnode(db, &node);
+ result = dns_dbiterator_next(it);
+ }
+
+ dns_dbiterator_destroy(&it);
+ dns_db_closeversion(db, &oldzone->dbversion, false);
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_MASTER, ISC_LOG_DEBUG(3),
+ "catz: update_from_db: iteration finished");
+
+ /*
+ * Finally merge new zone into old zone.
+ */
+ result = dns_catz_zones_merge(oldzone, newzone);
+ dns_catz_zone_detach(&newzone);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_MASTER,
+ ISC_LOG_ERROR,
+ "catz: failed merging zones: %s",
+ isc_result_totext(result));
+
+ return;
+ }
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_MASTER, ISC_LOG_DEBUG(3),
+ "catz: update_from_db: new zone merged");
+
+ /*
+ * When we're doing reconfig and setting a new catalog zone
+ * from an existing zone we won't have a chance to set up
+ * update callback in zone_startload or axfr_makedb, but we will
+ * call onupdate() artificially so we can register the callback here.
+ */
+ if (oldzone->db_registered == false) {
+ result = dns_db_updatenotify_register(db,
+ dns_catz_dbupdate_callback,
+ oldzone->catzs);
+ if (result == ISC_R_SUCCESS)
+ oldzone->db_registered = true;
+ }
+}
+
+void
+dns_catz_prereconfig(dns_catz_zones_t *catzs) {
+ isc_result_t result;
+ isc_ht_iter_t *iter = NULL;
+ dns_catz_zone_t *zone;
+
+ REQUIRE(catzs != NULL);
+
+ result = isc_ht_iter_create(catzs->zones, &iter);
+ INSIST(result == ISC_R_SUCCESS);
+ for (result = isc_ht_iter_first(iter);
+ result == ISC_R_SUCCESS;
+ result = isc_ht_iter_next(iter))
+ {
+ isc_ht_iter_current(iter, (void **) &zone);
+ zone->active = false;
+ }
+ INSIST(result == ISC_R_NOMORE);
+ isc_ht_iter_destroy(&iter);
+}
+
+void
+dns_catz_postreconfig(dns_catz_zones_t *catzs) {
+ isc_result_t result;
+ dns_catz_zone_t *newzone = NULL;
+ isc_ht_iter_t *iter = NULL;
+ dns_catz_zone_t *zone;
+
+ LOCK(&catzs->lock);
+ result = isc_ht_iter_create(catzs->zones, &iter);
+ INSIST(result == ISC_R_SUCCESS);
+ for (result = isc_ht_iter_first(iter);
+ result == ISC_R_SUCCESS;)
+ {
+ isc_ht_iter_current(iter, (void **) &zone);
+ if (zone->active == false) {
+ char cname[DNS_NAME_FORMATSIZE];
+ dns_name_format(&zone->name, cname,
+ DNS_NAME_FORMATSIZE);
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_MASTER,
+ ISC_LOG_WARNING,
+ "catz: removing catalog zone %s", cname);
+
+ /*
+ * Merge the old zone with an empty one to remove
+ * all members.
+ */
+ result = dns_catz_new_zone(catzs, &newzone,
+ &zone->name);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_catz_zones_merge(zone, newzone);
+ dns_catz_zone_detach(&newzone);
+
+ /* Make sure that we have an empty catalog zone. */
+ INSIST(isc_ht_count(zone->entries) == 0);
+ result = isc_ht_iter_delcurrent_next(iter);
+ dns_catz_zone_detach(&zone);
+ } else {
+ result = isc_ht_iter_next(iter);
+ }
+ }
+ UNLOCK(&catzs->lock);
+ RUNTIME_CHECK(result == ISC_R_NOMORE);
+ isc_ht_iter_destroy(&iter);
+}
+
+isc_result_t
+dns_catz_get_iterator(dns_catz_zone_t *catz, isc_ht_iter_t **itp) {
+ return (isc_ht_iter_create(catz->entries, itp));
+}
diff --git a/lib/dns/client.c b/lib/dns/client.c
new file mode 100644
index 0000000..a6d0e21
--- /dev/null
+++ b/lib/dns/client.c
@@ -0,0 +1,3243 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <config.h>
+
+#include <stdbool.h>
+#include <stddef.h>
+
+#include <isc/app.h>
+#include <isc/buffer.h>
+#include <isc/mem.h>
+#include <isc/mutex.h>
+#include <isc/safe.h>
+#include <isc/sockaddr.h>
+#include <isc/socket.h>
+#include <isc/task.h>
+#include <isc/timer.h>
+#include <isc/util.h>
+
+#include <dns/adb.h>
+#include <dns/client.h>
+#include <dns/db.h>
+#include <dns/dispatch.h>
+#include <dns/events.h>
+#include <dns/forward.h>
+#include <dns/keytable.h>
+#include <dns/message.h>
+#include <dns/name.h>
+#include <dns/rdata.h>
+#include <dns/rdatalist.h>
+#include <dns/rdataset.h>
+#include <dns/rdatatype.h>
+#include <dns/rdatasetiter.h>
+#include <dns/rdatastruct.h>
+#include <dns/request.h>
+#include <dns/resolver.h>
+#include <dns/result.h>
+#include <dns/tsec.h>
+#include <dns/tsig.h>
+#include <dns/view.h>
+
+#include <dst/dst.h>
+
+#define DNS_CLIENT_MAGIC ISC_MAGIC('D', 'N', 'S', 'c')
+#define DNS_CLIENT_VALID(c) ISC_MAGIC_VALID(c, DNS_CLIENT_MAGIC)
+
+#define RCTX_MAGIC ISC_MAGIC('R', 'c', 't', 'x')
+#define RCTX_VALID(c) ISC_MAGIC_VALID(c, RCTX_MAGIC)
+
+#define REQCTX_MAGIC ISC_MAGIC('R', 'q', 'c', 'x')
+#define REQCTX_VALID(c) ISC_MAGIC_VALID(c, REQCTX_MAGIC)
+
+#define UCTX_MAGIC ISC_MAGIC('U', 'c', 't', 'x')
+#define UCTX_VALID(c) ISC_MAGIC_VALID(c, UCTX_MAGIC)
+
+#define MAX_RESTARTS 16
+
+#ifdef TUNE_LARGE
+#define RESOLVER_NTASKS 523
+#else
+#define RESOLVER_NTASKS 31
+#endif /* TUNE_LARGE */
+
+/*%
+ * DNS client object
+ */
+struct dns_client {
+ /* Unlocked */
+ unsigned int magic;
+ unsigned int attributes;
+ isc_mutex_t lock;
+ isc_mem_t *mctx;
+ isc_appctx_t *actx;
+ isc_taskmgr_t *taskmgr;
+ isc_task_t *task;
+ isc_socketmgr_t *socketmgr;
+ isc_timermgr_t *timermgr;
+ dns_dispatchmgr_t *dispatchmgr;
+ dns_dispatch_t *dispatchv4;
+ dns_dispatch_t *dispatchv6;
+
+ unsigned int update_timeout;
+ unsigned int update_udptimeout;
+ unsigned int update_udpretries;
+ unsigned int find_timeout;
+ unsigned int find_udpretries;
+
+ /* Locked */
+ unsigned int references;
+ dns_viewlist_t viewlist;
+ ISC_LIST(struct resctx) resctxs;
+ ISC_LIST(struct reqctx) reqctxs;
+ ISC_LIST(struct updatectx) updatectxs;
+};
+
+/*%
+ * Timeout/retry constants for dynamic update borrowed from nsupdate
+ */
+#define DEF_UPDATE_TIMEOUT 300
+#define MIN_UPDATE_TIMEOUT 30
+#define DEF_UPDATE_UDPTIMEOUT 3
+#define DEF_UPDATE_UDPRETRIES 3
+
+#define DEF_FIND_TIMEOUT 5
+#define DEF_FIND_UDPRETRIES 3
+
+#define DNS_CLIENTATTR_OWNCTX 0x01
+
+#define DNS_CLIENTVIEW_NAME "dnsclient"
+
+/*%
+ * Internal state for a single name resolution procedure
+ */
+typedef struct resctx {
+ /* Unlocked */
+ unsigned int magic;
+ isc_mutex_t lock;
+ dns_client_t *client;
+ bool want_dnssec;
+ bool want_validation;
+ bool want_cdflag;
+ bool want_tcp;
+
+ /* Locked */
+ ISC_LINK(struct resctx) link;
+ isc_task_t *task;
+ dns_view_t *view;
+ unsigned int restarts;
+ dns_fixedname_t name;
+ dns_rdatatype_t type;
+ dns_fetch_t *fetch;
+ dns_namelist_t namelist;
+ isc_result_t result;
+ dns_clientresevent_t *event;
+ bool canceled;
+ dns_rdataset_t *rdataset;
+ dns_rdataset_t *sigrdataset;
+} resctx_t;
+
+/*%
+ * Argument of an internal event for synchronous name resolution.
+ */
+typedef struct resarg {
+ /* Unlocked */
+ isc_appctx_t *actx;
+ dns_client_t *client;
+ isc_mutex_t lock;
+
+ /* Locked */
+ isc_result_t result;
+ isc_result_t vresult;
+ dns_namelist_t *namelist;
+ dns_clientrestrans_t *trans;
+ bool canceled;
+} resarg_t;
+
+/*%
+ * Internal state for a single DNS request
+ */
+typedef struct reqctx {
+ /* Unlocked */
+ unsigned int magic;
+ isc_mutex_t lock;
+ dns_client_t *client;
+ unsigned int parseoptions;
+
+ /* Locked */
+ ISC_LINK(struct reqctx) link;
+ bool canceled;
+ dns_tsigkey_t *tsigkey;
+ dns_request_t *request;
+ dns_clientreqevent_t *event;
+} reqctx_t;
+
+/*%
+ * Argument of an internal event for synchronous DNS request.
+ */
+typedef struct reqarg {
+ /* Unlocked */
+ isc_appctx_t *actx;
+ dns_client_t *client;
+ isc_mutex_t lock;
+
+ /* Locked */
+ isc_result_t result;
+ dns_clientreqtrans_t *trans;
+ bool canceled;
+} reqarg_t;
+
+/*%
+ * Argument of an internal event for synchronous name resolution.
+ */
+typedef struct updatearg {
+ /* Unlocked */
+ isc_appctx_t *actx;
+ dns_client_t *client;
+ isc_mutex_t lock;
+
+ /* Locked */
+ isc_result_t result;
+ dns_clientupdatetrans_t *trans;
+ bool canceled;
+} updatearg_t;
+
+/*%
+ * Internal state for a single dynamic update procedure
+ */
+typedef struct updatectx {
+ /* Unlocked */
+ unsigned int magic;
+ isc_mutex_t lock;
+ dns_client_t *client;
+ bool want_tcp;
+
+ /* Locked */
+ dns_request_t *updatereq;
+ dns_request_t *soareq;
+ dns_clientrestrans_t *restrans;
+ dns_clientrestrans_t *restrans2;
+ bool canceled;
+
+ /* Task Locked */
+ ISC_LINK(struct updatectx) link;
+ dns_clientupdatestate_t state;
+ dns_rdataclass_t rdclass;
+ dns_view_t *view;
+ dns_message_t *updatemsg;
+ dns_message_t *soaquery;
+ dns_clientupdateevent_t *event;
+ dns_tsigkey_t *tsigkey;
+ dst_key_t *sig0key;
+ dns_name_t *firstname;
+ dns_name_t soaqname;
+ dns_fixedname_t zonefname;
+ dns_name_t *zonename;
+ isc_sockaddrlist_t servers;
+ unsigned int nservers;
+ isc_sockaddr_t *currentserver;
+ struct updatectx *bp4;
+ struct updatectx *bp6;
+} updatectx_t;
+
+static isc_result_t request_soa(updatectx_t *uctx);
+static void client_resfind(resctx_t *rctx, dns_fetchevent_t *event);
+static isc_result_t send_update(updatectx_t *uctx);
+
+static isc_result_t
+getudpdispatch(int family, dns_dispatchmgr_t *dispatchmgr,
+ isc_socketmgr_t *socketmgr, isc_taskmgr_t *taskmgr,
+ bool is_shared, dns_dispatch_t **dispp,
+ isc_sockaddr_t *localaddr)
+{
+ unsigned int attrs, attrmask;
+ dns_dispatch_t *disp;
+ unsigned buffersize, maxbuffers, maxrequests, buckets, increment;
+ isc_result_t result;
+ isc_sockaddr_t anyaddr;
+
+ attrs = 0;
+ attrs |= DNS_DISPATCHATTR_UDP;
+ switch (family) {
+ case AF_INET:
+ attrs |= DNS_DISPATCHATTR_IPV4;
+ break;
+ case AF_INET6:
+ attrs |= DNS_DISPATCHATTR_IPV6;
+ break;
+ default:
+ INSIST(0);
+ }
+ attrmask = 0;
+ attrmask |= DNS_DISPATCHATTR_UDP;
+ attrmask |= DNS_DISPATCHATTR_TCP;
+ attrmask |= DNS_DISPATCHATTR_IPV4;
+ attrmask |= DNS_DISPATCHATTR_IPV6;
+
+ if (localaddr == NULL) {
+ localaddr = &anyaddr;
+ isc_sockaddr_anyofpf(localaddr, family);
+ }
+
+ buffersize = 4096;
+ maxbuffers = is_shared ? 1000 : 8;
+ maxrequests = 32768;
+ buckets = is_shared ? 16411 : 3;
+ increment = is_shared ? 16433 : 5;
+
+ disp = NULL;
+ result = dns_dispatch_getudp(dispatchmgr, socketmgr,
+ taskmgr, localaddr,
+ buffersize, maxbuffers, maxrequests,
+ buckets, increment,
+ attrs, attrmask, &disp);
+ if (result == ISC_R_SUCCESS)
+ *dispp = disp;
+
+ return (result);
+}
+
+static isc_result_t
+createview(isc_mem_t *mctx, dns_rdataclass_t rdclass,
+ unsigned int options, isc_taskmgr_t *taskmgr,
+ unsigned int ntasks, isc_socketmgr_t *socketmgr,
+ isc_timermgr_t *timermgr, dns_dispatchmgr_t *dispatchmgr,
+ dns_dispatch_t *dispatchv4, dns_dispatch_t *dispatchv6,
+ dns_view_t **viewp)
+{
+ isc_result_t result;
+ dns_view_t *view = NULL;
+ const char *dbtype;
+
+ result = dns_view_create(mctx, rdclass, DNS_CLIENTVIEW_NAME, &view);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ /* Initialize view security roots */
+ result = dns_view_initsecroots(view, mctx);
+ if (result != ISC_R_SUCCESS) {
+ dns_view_detach(&view);
+ return (result);
+ }
+
+ result = dns_view_createresolver(view, taskmgr, ntasks, 1,
+ socketmgr, timermgr, 0,
+ dispatchmgr, dispatchv4, dispatchv6);
+ if (result != ISC_R_SUCCESS) {
+ dns_view_detach(&view);
+ return (result);
+ }
+
+ /*
+ * Set cache DB.
+ * XXX: it may be better if specific DB implementations can be
+ * specified via some configuration knob.
+ */
+ if ((options & DNS_CLIENTCREATEOPT_USECACHE) != 0)
+ dbtype = "rbt";
+ else
+ dbtype = "ecdb";
+ result = dns_db_create(mctx, dbtype, dns_rootname, dns_dbtype_cache,
+ rdclass, 0, NULL, &view->cachedb);
+ if (result != ISC_R_SUCCESS) {
+ dns_view_detach(&view);
+ return (result);
+ }
+
+ *viewp = view;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_client_create(dns_client_t **clientp, unsigned int options) {
+ isc_result_t result;
+ isc_mem_t *mctx = NULL;
+ isc_appctx_t *actx = NULL;
+ isc_taskmgr_t *taskmgr = NULL;
+ isc_socketmgr_t *socketmgr = NULL;
+ isc_timermgr_t *timermgr = NULL;
+#if 0
+ /* XXXMPA add debug logging support */
+ isc_log_t *lctx = NULL;
+ isc_logconfig_t *logconfig = NULL;
+ unsigned int logdebuglevel = 0;
+#endif
+
+ result = isc_mem_create(0, 0, &mctx);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ result = isc_appctx_create(mctx, &actx);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ result = isc_app_ctxstart(actx);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ result = isc_taskmgr_createinctx(mctx, actx, 1, 0, &taskmgr);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ result = isc_socketmgr_createinctx(mctx, actx, &socketmgr);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ result = isc_timermgr_createinctx(mctx, actx, &timermgr);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+#if 0
+ result = isc_log_create(mctx, &lctx, &logconfig);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ isc_log_setcontext(lctx);
+ dns_log_init(lctx);
+ dns_log_setcontext(lctx);
+ result = isc_log_usechannel(logconfig, "default_debug", NULL, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ isc_log_setdebuglevel(lctx, logdebuglevel);
+#endif
+ result = dns_client_createx(mctx, actx, taskmgr, socketmgr, timermgr,
+ options, clientp);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ (*clientp)->attributes |= DNS_CLIENTATTR_OWNCTX;
+
+ /* client has its own reference to mctx, so we can detach it here */
+ isc_mem_detach(&mctx);
+
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (taskmgr != NULL)
+ isc_taskmgr_destroy(&taskmgr);
+ if (timermgr != NULL)
+ isc_timermgr_destroy(&timermgr);
+ if (socketmgr != NULL)
+ isc_socketmgr_destroy(&socketmgr);
+ if (actx != NULL)
+ isc_appctx_destroy(&actx);
+ isc_mem_detach(&mctx);
+
+ return (result);
+}
+
+isc_result_t
+dns_client_createx(isc_mem_t *mctx, isc_appctx_t *actx, isc_taskmgr_t *taskmgr,
+ isc_socketmgr_t *socketmgr, isc_timermgr_t *timermgr,
+ unsigned int options, dns_client_t **clientp)
+{
+ isc_result_t result;
+ result = dns_client_createx2(mctx, actx, taskmgr, socketmgr, timermgr,
+ options, clientp, NULL, NULL);
+ return (result);
+}
+
+isc_result_t
+dns_client_createx2(isc_mem_t *mctx, isc_appctx_t *actx,
+ isc_taskmgr_t *taskmgr, isc_socketmgr_t *socketmgr,
+ isc_timermgr_t *timermgr, unsigned int options,
+ dns_client_t **clientp, isc_sockaddr_t *localaddr4,
+ isc_sockaddr_t *localaddr6)
+{
+ dns_client_t *client;
+ isc_result_t result;
+ dns_dispatchmgr_t *dispatchmgr = NULL;
+ dns_dispatch_t *dispatchv4 = NULL;
+ dns_dispatch_t *dispatchv6 = NULL;
+ dns_view_t *view = NULL;
+
+ REQUIRE(mctx != NULL);
+ REQUIRE(taskmgr != NULL);
+ REQUIRE(timermgr != NULL);
+ REQUIRE(socketmgr != NULL);
+ REQUIRE(clientp != NULL && *clientp == NULL);
+
+ client = isc_mem_get(mctx, sizeof(*client));
+ if (client == NULL)
+ return (ISC_R_NOMEMORY);
+
+ result = isc_mutex_init(&client->lock);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(mctx, client, sizeof(*client));
+ return (result);
+ }
+
+ client->actx = actx;
+ client->taskmgr = taskmgr;
+ client->socketmgr = socketmgr;
+ client->timermgr = timermgr;
+
+ client->task = NULL;
+ result = isc_task_create(client->taskmgr, 0, &client->task);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = dns_dispatchmgr_create(mctx, NULL, &dispatchmgr);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ client->dispatchmgr = dispatchmgr;
+
+ /*
+ * If only one address family is specified, use it.
+ * If neither family is specified, or if both are, use both.
+ */
+ client->dispatchv4 = NULL;
+ if (localaddr4 != NULL || localaddr6 == NULL) {
+ result = getudpdispatch(AF_INET, dispatchmgr, socketmgr,
+ taskmgr, true,
+ &dispatchv4, localaddr4);
+ if (result == ISC_R_SUCCESS)
+ client->dispatchv4 = dispatchv4;
+ }
+
+ client->dispatchv6 = NULL;
+ if (localaddr6 != NULL || localaddr4 == NULL) {
+ result = getudpdispatch(AF_INET6, dispatchmgr, socketmgr,
+ taskmgr, true,
+ &dispatchv6, localaddr6);
+ if (result == ISC_R_SUCCESS)
+ client->dispatchv6 = dispatchv6;
+ }
+
+ /* We need at least one of the dispatchers */
+ if (dispatchv4 == NULL && dispatchv6 == NULL) {
+ INSIST(result != ISC_R_SUCCESS);
+ goto cleanup;
+ }
+
+ /* Create the default view for class IN */
+ result = createview(mctx, dns_rdataclass_in, options, taskmgr,
+ RESOLVER_NTASKS, socketmgr, timermgr,
+ dispatchmgr, dispatchv4, dispatchv6, &view);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ ISC_LIST_INIT(client->viewlist);
+ ISC_LIST_APPEND(client->viewlist, view, link);
+
+ dns_view_freeze(view); /* too early? */
+
+ ISC_LIST_INIT(client->resctxs);
+ ISC_LIST_INIT(client->reqctxs);
+ ISC_LIST_INIT(client->updatectxs);
+
+ client->mctx = NULL;
+ isc_mem_attach(mctx, &client->mctx);
+
+ client->update_timeout = DEF_UPDATE_TIMEOUT;
+ client->update_udptimeout = DEF_UPDATE_UDPTIMEOUT;
+ client->update_udpretries = DEF_UPDATE_UDPRETRIES;
+ client->find_timeout = DEF_FIND_TIMEOUT;
+ client->find_udpretries = DEF_FIND_UDPRETRIES;
+ client->attributes = 0;
+
+ client->references = 1;
+ client->magic = DNS_CLIENT_MAGIC;
+
+ *clientp = client;
+
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (dispatchv4 != NULL)
+ dns_dispatch_detach(&dispatchv4);
+ if (dispatchv6 != NULL)
+ dns_dispatch_detach(&dispatchv6);
+ if (dispatchmgr != NULL)
+ dns_dispatchmgr_destroy(&dispatchmgr);
+ if (client->task != NULL)
+ isc_task_detach(&client->task);
+ isc_mem_put(mctx, client, sizeof(*client));
+
+ return (result);
+}
+
+static void
+destroyclient(dns_client_t **clientp) {
+ dns_client_t *client = *clientp;
+ dns_view_t *view;
+
+ while ((view = ISC_LIST_HEAD(client->viewlist)) != NULL) {
+ ISC_LIST_UNLINK(client->viewlist, view, link);
+ dns_view_detach(&view);
+ }
+
+ if (client->dispatchv4 != NULL)
+ dns_dispatch_detach(&client->dispatchv4);
+ if (client->dispatchv6 != NULL)
+ dns_dispatch_detach(&client->dispatchv6);
+
+ dns_dispatchmgr_destroy(&client->dispatchmgr);
+
+ isc_task_detach(&client->task);
+
+ /*
+ * If the client has created its own running environments,
+ * destroy them.
+ */
+ if ((client->attributes & DNS_CLIENTATTR_OWNCTX) != 0) {
+ isc_taskmgr_destroy(&client->taskmgr);
+ isc_timermgr_destroy(&client->timermgr);
+ isc_socketmgr_destroy(&client->socketmgr);
+
+ isc_app_ctxfinish(client->actx);
+ isc_appctx_destroy(&client->actx);
+ }
+
+ DESTROYLOCK(&client->lock);
+ client->magic = 0;
+
+ isc_mem_putanddetach(&client->mctx, client, sizeof(*client));
+
+ *clientp = NULL;
+}
+
+void
+dns_client_destroy(dns_client_t **clientp) {
+ dns_client_t *client;
+ bool destroyok = false;
+
+ REQUIRE(clientp != NULL);
+ client = *clientp;
+ REQUIRE(DNS_CLIENT_VALID(client));
+
+ LOCK(&client->lock);
+ client->references--;
+ if (client->references == 0 && ISC_LIST_EMPTY(client->resctxs) &&
+ ISC_LIST_EMPTY(client->reqctxs) &&
+ ISC_LIST_EMPTY(client->updatectxs)) {
+ destroyok = true;
+ }
+ UNLOCK(&client->lock);
+
+ if (destroyok)
+ destroyclient(&client);
+
+ *clientp = NULL;
+}
+
+isc_result_t
+dns_client_setservers(dns_client_t *client, dns_rdataclass_t rdclass,
+ dns_name_t *name_space, isc_sockaddrlist_t *addrs)
+{
+ isc_result_t result;
+ dns_view_t *view = NULL;
+
+ REQUIRE(DNS_CLIENT_VALID(client));
+ REQUIRE(addrs != NULL);
+
+ if (name_space == NULL)
+ name_space = dns_rootname;
+
+ LOCK(&client->lock);
+ result = dns_viewlist_find(&client->viewlist, DNS_CLIENTVIEW_NAME,
+ rdclass, &view);
+ if (result != ISC_R_SUCCESS) {
+ UNLOCK(&client->lock);
+ return (result);
+ }
+ UNLOCK(&client->lock);
+
+ result = dns_fwdtable_add(view->fwdtable, name_space, addrs,
+ dns_fwdpolicy_only);
+
+ dns_view_detach(&view);
+
+ return (result);
+}
+
+isc_result_t
+dns_client_clearservers(dns_client_t *client, dns_rdataclass_t rdclass,
+ dns_name_t *name_space)
+{
+ isc_result_t result;
+ dns_view_t *view = NULL;
+
+ REQUIRE(DNS_CLIENT_VALID(client));
+
+ if (name_space == NULL)
+ name_space = dns_rootname;
+
+ LOCK(&client->lock);
+ result = dns_viewlist_find(&client->viewlist, DNS_CLIENTVIEW_NAME,
+ rdclass, &view);
+ if (result != ISC_R_SUCCESS) {
+ UNLOCK(&client->lock);
+ return (result);
+ }
+ UNLOCK(&client->lock);
+
+ result = dns_fwdtable_delete(view->fwdtable, name_space);
+
+ dns_view_detach(&view);
+
+ return (result);
+}
+
+isc_result_t
+dns_client_setdlv(dns_client_t *client, dns_rdataclass_t rdclass,
+ const char *dlvname)
+{
+ isc_result_t result;
+ isc_buffer_t b;
+ dns_view_t *view = NULL;
+
+ REQUIRE(DNS_CLIENT_VALID(client));
+
+ LOCK(&client->lock);
+ result = dns_viewlist_find(&client->viewlist, DNS_CLIENTVIEW_NAME,
+ rdclass, &view);
+ UNLOCK(&client->lock);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ if (dlvname == NULL)
+ view->dlv = NULL;
+ else {
+ dns_name_t *newdlv;
+
+ isc_buffer_constinit(&b, dlvname, strlen(dlvname));
+ isc_buffer_add(&b, strlen(dlvname));
+ newdlv = dns_fixedname_name(&view->dlv_fixed);
+ result = dns_name_fromtext(newdlv, &b, dns_rootname,
+ DNS_NAME_DOWNCASE, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ view->dlv = dns_fixedname_name(&view->dlv_fixed);
+ }
+
+ cleanup:
+ if (view != NULL)
+ dns_view_detach(&view);
+
+ return (result);
+}
+
+static isc_result_t
+getrdataset(isc_mem_t *mctx, dns_rdataset_t **rdatasetp) {
+ dns_rdataset_t *rdataset;
+
+ REQUIRE(mctx != NULL);
+ REQUIRE(rdatasetp != NULL && *rdatasetp == NULL);
+
+ rdataset = isc_mem_get(mctx, sizeof(*rdataset));
+ if (rdataset == NULL)
+ return (ISC_R_NOMEMORY);
+
+ dns_rdataset_init(rdataset);
+
+ *rdatasetp = rdataset;
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+putrdataset(isc_mem_t *mctx, dns_rdataset_t **rdatasetp) {
+ dns_rdataset_t *rdataset;
+
+ REQUIRE(rdatasetp != NULL);
+ rdataset = *rdatasetp;
+ REQUIRE(rdataset != NULL);
+
+ if (dns_rdataset_isassociated(rdataset))
+ dns_rdataset_disassociate(rdataset);
+
+ isc_mem_put(mctx, rdataset, sizeof(*rdataset));
+
+ *rdatasetp = NULL;
+}
+
+static void
+fetch_done(isc_task_t *task, isc_event_t *event) {
+ resctx_t *rctx = event->ev_arg;
+ dns_fetchevent_t *fevent;
+
+ REQUIRE(event->ev_type == DNS_EVENT_FETCHDONE);
+ REQUIRE(RCTX_VALID(rctx));
+ REQUIRE(rctx->task == task);
+ fevent = (dns_fetchevent_t *)event;
+
+ client_resfind(rctx, fevent);
+}
+
+static inline isc_result_t
+start_fetch(resctx_t *rctx) {
+ isc_result_t result;
+ int fopts = 0;
+
+ /*
+ * The caller must be holding the rctx's lock.
+ */
+
+ REQUIRE(rctx->fetch == NULL);
+
+ if (!rctx->want_cdflag)
+ fopts |= DNS_FETCHOPT_NOCDFLAG;
+ if (!rctx->want_validation)
+ fopts |= DNS_FETCHOPT_NOVALIDATE;
+ if (rctx->want_tcp)
+ fopts |= DNS_FETCHOPT_TCP;
+
+ result = dns_resolver_createfetch(rctx->view->resolver,
+ dns_fixedname_name(&rctx->name),
+ rctx->type,
+ NULL, NULL, NULL, fopts,
+ rctx->task, fetch_done, rctx,
+ rctx->rdataset,
+ rctx->sigrdataset,
+ &rctx->fetch);
+
+ return (result);
+}
+
+static isc_result_t
+view_find(resctx_t *rctx, dns_db_t **dbp, dns_dbnode_t **nodep,
+ dns_name_t *foundname)
+{
+ isc_result_t result;
+ dns_name_t *name = dns_fixedname_name(&rctx->name);
+ dns_rdatatype_t type;
+
+ if (rctx->type == dns_rdatatype_rrsig)
+ type = dns_rdatatype_any;
+ else
+ type = rctx->type;
+
+ result = dns_view_find(rctx->view, name, type, 0, 0, false,
+ dbp, nodep, foundname, rctx->rdataset,
+ rctx->sigrdataset);
+
+ return (result);
+}
+
+static void
+client_resfind(resctx_t *rctx, dns_fetchevent_t *event) {
+ isc_mem_t *mctx;
+ isc_result_t tresult, result = ISC_R_SUCCESS;
+ isc_result_t vresult = ISC_R_SUCCESS;
+ bool want_restart;
+ bool send_event = false;
+ dns_name_t *name, *prefix;
+ dns_fixedname_t foundname, fixed;
+ dns_rdataset_t *trdataset;
+ 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(RCTX_VALID(rctx));
+
+ LOCK(&rctx->lock);
+
+ mctx = rctx->view->mctx;
+
+ name = dns_fixedname_name(&rctx->name);
+
+ do {
+ dns_name_t *fname = NULL;
+ dns_name_t *ansname = NULL;
+ dns_db_t *db = NULL;
+ dns_dbnode_t *node = NULL;
+
+ rctx->restarts++;
+ want_restart = false;
+
+ if (event == NULL && !rctx->canceled) {
+ fname = dns_fixedname_initname(&foundname);
+ INSIST(!dns_rdataset_isassociated(rctx->rdataset));
+ INSIST(rctx->sigrdataset == NULL ||
+ !dns_rdataset_isassociated(rctx->sigrdataset));
+ result = view_find(rctx, &db, &node, fname);
+ if (result == ISC_R_NOTFOUND) {
+ /*
+ * We don't know anything about the name.
+ * Launch a fetch.
+ */
+ if (node != NULL) {
+ INSIST(db != NULL);
+ dns_db_detachnode(db, &node);
+ }
+ if (db != NULL)
+ dns_db_detach(&db);
+ result = start_fetch(rctx);
+ if (result != ISC_R_SUCCESS) {
+ putrdataset(mctx, &rctx->rdataset);
+ if (rctx->sigrdataset != NULL)
+ putrdataset(mctx,
+ &rctx->sigrdataset);
+ send_event = true;
+ }
+ goto done;
+ }
+ } else {
+ INSIST(event != NULL);
+ INSIST(event->fetch == rctx->fetch);
+ dns_resolver_destroyfetch(&rctx->fetch);
+ db = event->db;
+ node = event->node;
+ result = event->result;
+ vresult = event->vresult;
+ fname = dns_fixedname_name(&event->foundname);
+ INSIST(event->rdataset == rctx->rdataset);
+ INSIST(event->sigrdataset == rctx->sigrdataset);
+ }
+
+ /*
+ * If we've been canceled, forget about the result.
+ */
+ if (rctx->canceled)
+ result = ISC_R_CANCELED;
+ else {
+ /*
+ * Otherwise, get some resource for copying the
+ * result.
+ */
+ ansname = isc_mem_get(mctx, sizeof(*ansname));
+ if (ansname == NULL)
+ tresult = ISC_R_NOMEMORY;
+ else {
+ dns_name_t *aname;
+
+ aname = dns_fixedname_name(&rctx->name);
+ dns_name_init(ansname, NULL);
+ tresult = dns_name_dup(aname, mctx, ansname);
+ if (tresult != ISC_R_SUCCESS)
+ isc_mem_put(mctx, ansname,
+ sizeof(*ansname));
+ }
+ if (tresult != ISC_R_SUCCESS)
+ result = tresult;
+ }
+
+ switch (result) {
+ case ISC_R_SUCCESS:
+ send_event = true;
+ /*
+ * This case is handled in the main line below.
+ */
+ break;
+ case DNS_R_CNAME:
+ /*
+ * Add the CNAME to the answer list.
+ */
+ trdataset = rctx->rdataset;
+ ISC_LIST_APPEND(ansname->list, rctx->rdataset, link);
+ rctx->rdataset = NULL;
+ if (rctx->sigrdataset != NULL) {
+ ISC_LIST_APPEND(ansname->list,
+ rctx->sigrdataset, link);
+ rctx->sigrdataset = NULL;
+ }
+ ISC_LIST_APPEND(rctx->namelist, ansname, link);
+ ansname = NULL;
+
+ /*
+ * Copy the CNAME's target into the lookup's
+ * query name and start over.
+ */
+ tresult = dns_rdataset_first(trdataset);
+ if (tresult != ISC_R_SUCCESS)
+ goto done;
+ dns_rdataset_current(trdataset, &rdata);
+ tresult = dns_rdata_tostruct(&rdata, &cname, NULL);
+ dns_rdata_reset(&rdata);
+ if (tresult != ISC_R_SUCCESS)
+ goto done;
+ tresult = dns_name_copy(&cname.cname, name, NULL);
+ dns_rdata_freestruct(&cname);
+ if (tresult == ISC_R_SUCCESS)
+ want_restart = true;
+ else
+ result = tresult;
+ goto done;
+ case DNS_R_DNAME:
+ /*
+ * Add the DNAME to the answer list.
+ */
+ trdataset = rctx->rdataset;
+ ISC_LIST_APPEND(ansname->list, rctx->rdataset, link);
+ rctx->rdataset = NULL;
+ if (rctx->sigrdataset != NULL) {
+ ISC_LIST_APPEND(ansname->list,
+ rctx->sigrdataset, link);
+ rctx->sigrdataset = NULL;
+ }
+ ISC_LIST_APPEND(rctx->namelist, ansname, link);
+ ansname = NULL;
+
+ namereln = dns_name_fullcompare(name, fname, &order,
+ &nlabels);
+ INSIST(namereln == dns_namereln_subdomain);
+ /*
+ * Get the target name of the DNAME.
+ */
+ tresult = dns_rdataset_first(trdataset);
+ if (tresult != ISC_R_SUCCESS) {
+ result = tresult;
+ goto done;
+ }
+ dns_rdataset_current(trdataset, &rdata);
+ tresult = dns_rdata_tostruct(&rdata, &dname, NULL);
+ dns_rdata_reset(&rdata);
+ if (tresult != ISC_R_SUCCESS) {
+ result = tresult;
+ goto done;
+ }
+ /*
+ * Construct the new query name and start over.
+ */
+ prefix = dns_fixedname_initname(&fixed);
+ dns_name_split(name, nlabels, prefix, NULL);
+ tresult = dns_name_concatenate(prefix, &dname.dname,
+ name, NULL);
+ dns_rdata_freestruct(&dname);
+ if (tresult == ISC_R_SUCCESS)
+ want_restart = true;
+ else
+ result = tresult;
+ goto done;
+ case DNS_R_NCACHENXDOMAIN:
+ case DNS_R_NCACHENXRRSET:
+ ISC_LIST_APPEND(ansname->list, rctx->rdataset, link);
+ ISC_LIST_APPEND(rctx->namelist, ansname, link);
+ ansname = NULL;
+ rctx->rdataset = NULL;
+ /* What about sigrdataset? */
+ if (rctx->sigrdataset != NULL)
+ putrdataset(mctx, &rctx->sigrdataset);
+ send_event = true;
+ goto done;
+ default:
+ if (rctx->rdataset != NULL)
+ putrdataset(mctx, &rctx->rdataset);
+ if (rctx->sigrdataset != NULL)
+ putrdataset(mctx, &rctx->sigrdataset);
+ send_event = true;
+ goto done;
+ }
+
+ if (rctx->type == dns_rdatatype_any) {
+ int n = 0;
+ dns_rdatasetiter_t *rdsiter = NULL;
+
+ tresult = dns_db_allrdatasets(db, node, NULL, 0,
+ &rdsiter);
+ if (tresult != ISC_R_SUCCESS) {
+ result = tresult;
+ goto done;
+ }
+
+ tresult = dns_rdatasetiter_first(rdsiter);
+ while (tresult == ISC_R_SUCCESS) {
+ dns_rdatasetiter_current(rdsiter,
+ rctx->rdataset);
+ if (rctx->rdataset->type != 0) {
+ ISC_LIST_APPEND(ansname->list,
+ rctx->rdataset,
+ link);
+ n++;
+ rctx->rdataset = NULL;
+ } else {
+ /*
+ * We're not interested in this
+ * rdataset.
+ */
+ dns_rdataset_disassociate(
+ rctx->rdataset);
+ }
+ tresult = dns_rdatasetiter_next(rdsiter);
+
+ if (tresult == ISC_R_SUCCESS &&
+ rctx->rdataset == NULL) {
+ tresult = getrdataset(mctx,
+ &rctx->rdataset);
+ if (tresult != ISC_R_SUCCESS) {
+ result = tresult;
+ POST(result);
+ break;
+ }
+ }
+ }
+ if (n == 0) {
+ /*
+ * We didn't match any rdatasets (which means
+ * something went wrong in this
+ * implementation).
+ */
+ result = DNS_R_SERVFAIL; /* better code? */
+ POST(result);
+ } else {
+ ISC_LIST_APPEND(rctx->namelist, ansname, link);
+ ansname = NULL;
+ }
+ dns_rdatasetiter_destroy(&rdsiter);
+ if (tresult != ISC_R_NOMORE)
+ result = DNS_R_SERVFAIL; /* ditto */
+ else
+ result = ISC_R_SUCCESS;
+ goto done;
+ } else {
+ /*
+ * This is the "normal" case -- an ordinary question
+ * to which we've got the answer.
+ */
+ ISC_LIST_APPEND(ansname->list, rctx->rdataset, link);
+ rctx->rdataset = NULL;
+ if (rctx->sigrdataset != NULL) {
+ ISC_LIST_APPEND(ansname->list,
+ rctx->sigrdataset, link);
+ rctx->sigrdataset = NULL;
+ }
+ ISC_LIST_APPEND(rctx->namelist, ansname, link);
+ ansname = NULL;
+ }
+
+ done:
+ /*
+ * Free temporary resources
+ */
+ if (ansname != NULL) {
+ dns_rdataset_t *rdataset;
+
+ while ((rdataset = ISC_LIST_HEAD(ansname->list))
+ != NULL) {
+ ISC_LIST_UNLINK(ansname->list, rdataset, link);
+ putrdataset(mctx, &rdataset);
+ }
+ dns_name_free(ansname, mctx);
+ isc_mem_put(mctx, ansname, sizeof(*ansname));
+ }
+
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ if (db != NULL)
+ dns_db_detach(&db);
+ if (event != NULL)
+ isc_event_free(ISC_EVENT_PTR(&event));
+
+ /*
+ * Limit the number of restarts.
+ */
+ if (want_restart && rctx->restarts == MAX_RESTARTS) {
+ want_restart = false;
+ result = ISC_R_QUOTA;
+ send_event = true;
+ }
+
+ /*
+ * Prepare further find with new resources
+ */
+ if (want_restart) {
+ INSIST(rctx->rdataset == NULL &&
+ rctx->sigrdataset == NULL);
+
+ result = getrdataset(mctx, &rctx->rdataset);
+ if (result == ISC_R_SUCCESS && rctx->want_dnssec) {
+ result = getrdataset(mctx, &rctx->sigrdataset);
+ if (result != ISC_R_SUCCESS) {
+ putrdataset(mctx, &rctx->rdataset);
+ }
+ }
+
+ if (result != ISC_R_SUCCESS) {
+ want_restart = false;
+ send_event = true;
+ }
+ }
+ } while (want_restart);
+
+ if (send_event) {
+ isc_task_t *task;
+
+ while ((name = ISC_LIST_HEAD(rctx->namelist)) != NULL) {
+ ISC_LIST_UNLINK(rctx->namelist, name, link);
+ ISC_LIST_APPEND(rctx->event->answerlist, name, link);
+ }
+
+ rctx->event->result = result;
+ rctx->event->vresult = vresult;
+ task = rctx->event->ev_sender;
+ rctx->event->ev_sender = rctx;
+ isc_task_sendanddetach(&task, ISC_EVENT_PTR(&rctx->event));
+ }
+
+ UNLOCK(&rctx->lock);
+}
+
+static void
+suspend(isc_task_t *task, isc_event_t *event) {
+ isc_appctx_t *actx = event->ev_arg;
+
+ UNUSED(task);
+
+ isc_app_ctxsuspend(actx);
+ isc_event_free(&event);
+}
+
+static void
+resolve_done(isc_task_t *task, isc_event_t *event) {
+ resarg_t *resarg = event->ev_arg;
+ dns_clientresevent_t *rev = (dns_clientresevent_t *)event;
+ dns_name_t *name;
+ isc_result_t result;
+
+ UNUSED(task);
+
+ LOCK(&resarg->lock);
+
+ resarg->result = rev->result;
+ resarg->vresult = rev->vresult;
+ while ((name = ISC_LIST_HEAD(rev->answerlist)) != NULL) {
+ ISC_LIST_UNLINK(rev->answerlist, name, link);
+ ISC_LIST_APPEND(*resarg->namelist, name, link);
+ }
+
+ dns_client_destroyrestrans(&resarg->trans);
+ isc_event_free(&event);
+
+ if (!resarg->canceled) {
+ UNLOCK(&resarg->lock);
+
+ /*
+ * We may or may not be running. isc__appctx_onrun will
+ * fail if we are currently running otherwise we post a
+ * action to call isc_app_ctxsuspend when we do start
+ * running.
+ */
+ result = isc_app_ctxonrun(resarg->actx, resarg->client->mctx,
+ task, suspend, resarg->actx);
+ if (result == ISC_R_ALREADYRUNNING)
+ isc_app_ctxsuspend(resarg->actx);
+ } else {
+ /*
+ * We have already exited from the loop (due to some
+ * unexpected event). Just clean the arg up.
+ */
+ UNLOCK(&resarg->lock);
+ DESTROYLOCK(&resarg->lock);
+ isc_mem_put(resarg->client->mctx, resarg, sizeof(*resarg));
+ }
+}
+
+isc_result_t
+dns_client_resolve(dns_client_t *client, dns_name_t *name,
+ dns_rdataclass_t rdclass, dns_rdatatype_t type,
+ unsigned int options, dns_namelist_t *namelist)
+{
+ isc_result_t result;
+ isc_appctx_t *actx;
+ resarg_t *resarg;
+
+ REQUIRE(DNS_CLIENT_VALID(client));
+ REQUIRE(namelist != NULL && ISC_LIST_EMPTY(*namelist));
+
+ if ((client->attributes & DNS_CLIENTATTR_OWNCTX) == 0 &&
+ (options & DNS_CLIENTRESOPT_ALLOWRUN) == 0) {
+ /*
+ * If the client is run under application's control, we need
+ * to create a new running (sub)environment for this
+ * particular resolution.
+ */
+ return (ISC_R_NOTIMPLEMENTED); /* XXXTBD */
+ } else
+ actx = client->actx;
+
+ resarg = isc_mem_get(client->mctx, sizeof(*resarg));
+ if (resarg == NULL)
+ return (ISC_R_NOMEMORY);
+
+ result = isc_mutex_init(&resarg->lock);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(client->mctx, resarg, sizeof(*resarg));
+ return (result);
+ }
+
+ resarg->actx = actx;
+ resarg->client = client;
+ resarg->result = DNS_R_SERVFAIL;
+ resarg->namelist = namelist;
+ resarg->trans = NULL;
+ resarg->canceled = false;
+ result = dns_client_startresolve(client, name, rdclass, type, options,
+ client->task, resolve_done, resarg,
+ &resarg->trans);
+ if (result != ISC_R_SUCCESS) {
+ DESTROYLOCK(&resarg->lock);
+ isc_mem_put(client->mctx, resarg, sizeof(*resarg));
+ return (result);
+ }
+
+ /*
+ * Start internal event loop. It blocks until the entire process
+ * is completed.
+ */
+ result = isc_app_ctxrun(actx);
+
+ LOCK(&resarg->lock);
+ if (result == ISC_R_SUCCESS || result == ISC_R_SUSPEND)
+ result = resarg->result;
+ if (result != ISC_R_SUCCESS && resarg->vresult != ISC_R_SUCCESS) {
+ /*
+ * If this lookup failed due to some error in DNSSEC
+ * validation, return the validation error code.
+ * XXX: or should we pass the validation result separately?
+ */
+ result = resarg->vresult;
+ }
+ if (resarg->trans != NULL) {
+ /*
+ * Unusual termination (perhaps due to signal). We need some
+ * tricky cleanup process.
+ */
+ resarg->canceled = true;
+ dns_client_cancelresolve(resarg->trans);
+
+ UNLOCK(&resarg->lock);
+
+ /* resarg will be freed in the event handler. */
+ } else {
+ UNLOCK(&resarg->lock);
+
+ DESTROYLOCK(&resarg->lock);
+ isc_mem_put(client->mctx, resarg, sizeof(*resarg));
+ }
+
+ return (result);
+}
+
+isc_result_t
+dns_client_startresolve(dns_client_t *client, dns_name_t *name,
+ dns_rdataclass_t rdclass, dns_rdatatype_t type,
+ unsigned int options, isc_task_t *task,
+ isc_taskaction_t action, void *arg,
+ dns_clientrestrans_t **transp)
+{
+ dns_view_t *view = NULL;
+ dns_clientresevent_t *event = NULL;
+ resctx_t *rctx = NULL;
+ isc_task_t *tclone = NULL;
+ isc_mem_t *mctx;
+ isc_result_t result;
+ dns_rdataset_t *rdataset, *sigrdataset;
+ bool want_dnssec, want_validation, want_cdflag, want_tcp;
+
+ REQUIRE(DNS_CLIENT_VALID(client));
+ REQUIRE(transp != NULL && *transp == NULL);
+
+ LOCK(&client->lock);
+ result = dns_viewlist_find(&client->viewlist, DNS_CLIENTVIEW_NAME,
+ rdclass, &view);
+ UNLOCK(&client->lock);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ mctx = client->mctx;
+ rdataset = NULL;
+ sigrdataset = NULL;
+ want_dnssec = !(options & DNS_CLIENTRESOPT_NODNSSEC);
+ want_validation = !(options & DNS_CLIENTRESOPT_NOVALIDATE);
+ want_cdflag = !(options & DNS_CLIENTRESOPT_NOCDFLAG);
+ want_tcp = (options & DNS_CLIENTRESOPT_TCP);
+
+ /*
+ * Prepare some intermediate resources
+ */
+ tclone = NULL;
+ isc_task_attach(task, &tclone);
+ event = (dns_clientresevent_t *)
+ isc_event_allocate(mctx, tclone, DNS_EVENT_CLIENTRESDONE,
+ action, arg, sizeof(*event));
+ if (event == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ event->result = DNS_R_SERVFAIL;
+ ISC_LIST_INIT(event->answerlist);
+
+ rctx = isc_mem_get(mctx, sizeof(*rctx));
+ if (rctx == NULL)
+ result = ISC_R_NOMEMORY;
+ else {
+ result = isc_mutex_init(&rctx->lock);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(mctx, rctx, sizeof(*rctx));
+ rctx = NULL;
+ }
+ }
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = getrdataset(mctx, &rdataset);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ rctx->rdataset = rdataset;
+
+ if (want_dnssec) {
+ result = getrdataset(mctx, &sigrdataset);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+ rctx->sigrdataset = sigrdataset;
+
+ dns_fixedname_init(&rctx->name);
+ result = dns_name_copy(name, dns_fixedname_name(&rctx->name), NULL);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ rctx->client = client;
+ ISC_LINK_INIT(rctx, link);
+ rctx->canceled = false;
+ rctx->task = client->task;
+ rctx->type = type;
+ rctx->view = view;
+ rctx->restarts = 0;
+ rctx->fetch = NULL;
+ rctx->want_dnssec = want_dnssec;
+ rctx->want_validation = want_validation;
+ rctx->want_cdflag = want_cdflag;
+ rctx->want_tcp = want_tcp;
+ ISC_LIST_INIT(rctx->namelist);
+ rctx->event = event;
+
+ rctx->magic = RCTX_MAGIC;
+
+ LOCK(&client->lock);
+ ISC_LIST_APPEND(client->resctxs, rctx, link);
+ UNLOCK(&client->lock);
+
+ *transp = (dns_clientrestrans_t *)rctx;
+ client_resfind(rctx, NULL);
+
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (rdataset != NULL)
+ putrdataset(client->mctx, &rdataset);
+ if (sigrdataset != NULL)
+ putrdataset(client->mctx, &sigrdataset);
+ if (rctx != NULL) {
+ DESTROYLOCK(&rctx->lock);
+ isc_mem_put(mctx, rctx, sizeof(*rctx));
+ }
+ if (event != NULL)
+ isc_event_free(ISC_EVENT_PTR(&event));
+ isc_task_detach(&tclone);
+ dns_view_detach(&view);
+
+ return (result);
+}
+
+void
+dns_client_cancelresolve(dns_clientrestrans_t *trans) {
+ resctx_t *rctx;
+
+ REQUIRE(trans != NULL);
+ rctx = (resctx_t *)trans;
+ REQUIRE(RCTX_VALID(rctx));
+
+ LOCK(&rctx->lock);
+
+ if (!rctx->canceled) {
+ rctx->canceled = true;
+ if (rctx->fetch != NULL)
+ dns_resolver_cancelfetch(rctx->fetch);
+ }
+
+ UNLOCK(&rctx->lock);
+}
+
+void
+dns_client_freeresanswer(dns_client_t *client, dns_namelist_t *namelist) {
+ dns_name_t *name;
+ dns_rdataset_t *rdataset;
+
+ REQUIRE(DNS_CLIENT_VALID(client));
+ REQUIRE(namelist != NULL);
+
+ while ((name = ISC_LIST_HEAD(*namelist)) != NULL) {
+ ISC_LIST_UNLINK(*namelist, name, link);
+ while ((rdataset = ISC_LIST_HEAD(name->list)) != NULL) {
+ ISC_LIST_UNLINK(name->list, rdataset, link);
+ putrdataset(client->mctx, &rdataset);
+ }
+ dns_name_free(name, client->mctx);
+ isc_mem_put(client->mctx, name, sizeof(*name));
+ }
+}
+
+void
+dns_client_destroyrestrans(dns_clientrestrans_t **transp) {
+ resctx_t *rctx;
+ isc_mem_t *mctx;
+ dns_client_t *client;
+ bool need_destroyclient = false;
+
+ REQUIRE(transp != NULL);
+ rctx = (resctx_t *)*transp;
+ REQUIRE(RCTX_VALID(rctx));
+ REQUIRE(rctx->fetch == NULL);
+ REQUIRE(rctx->event == NULL);
+ client = rctx->client;
+ REQUIRE(DNS_CLIENT_VALID(client));
+
+ mctx = client->mctx;
+ dns_view_detach(&rctx->view);
+
+ /*
+ * Wait for the lock in client_resfind to be released before
+ * destroying the lock.
+ */
+ LOCK(&rctx->lock);
+ UNLOCK(&rctx->lock);
+
+ LOCK(&client->lock);
+
+ INSIST(ISC_LINK_LINKED(rctx, link));
+ ISC_LIST_UNLINK(client->resctxs, rctx, link);
+
+ if (client->references == 0 && ISC_LIST_EMPTY(client->resctxs) &&
+ ISC_LIST_EMPTY(client->reqctxs) &&
+ ISC_LIST_EMPTY(client->updatectxs))
+ need_destroyclient = true;
+
+ UNLOCK(&client->lock);
+
+ INSIST(ISC_LIST_EMPTY(rctx->namelist));
+
+ DESTROYLOCK(&rctx->lock);
+ rctx->magic = 0;
+
+ isc_mem_put(mctx, rctx, sizeof(*rctx));
+
+ if (need_destroyclient)
+ destroyclient(&client);
+
+ *transp = NULL;
+}
+
+isc_result_t
+dns_client_addtrustedkey(dns_client_t *client, dns_rdataclass_t rdclass,
+ dns_name_t *keyname, isc_buffer_t *keydatabuf)
+{
+ isc_result_t result;
+ dns_view_t *view = NULL;
+ dst_key_t *dstkey = NULL;
+ dns_keytable_t *secroots = NULL;
+
+ REQUIRE(DNS_CLIENT_VALID(client));
+
+ LOCK(&client->lock);
+ result = dns_viewlist_find(&client->viewlist, DNS_CLIENTVIEW_NAME,
+ rdclass, &view);
+ UNLOCK(&client->lock);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = dns_view_getsecroots(view, &secroots);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = dst_key_fromdns(keyname, rdclass, keydatabuf, client->mctx,
+ &dstkey);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = dns_keytable_add(secroots, false, &dstkey);
+
+ cleanup:
+ if (dstkey != NULL)
+ dst_key_free(&dstkey);
+ if (view != NULL)
+ dns_view_detach(&view);
+ if (secroots != NULL)
+ dns_keytable_detach(&secroots);
+ return (result);
+}
+
+/*%
+ * Simple request routines
+ */
+static void
+request_done(isc_task_t *task, isc_event_t *event) {
+ dns_requestevent_t *reqev = NULL;
+ dns_request_t *request;
+ isc_result_t result, eresult;
+ reqctx_t *ctx;
+
+ UNUSED(task);
+
+ REQUIRE(event->ev_type == DNS_EVENT_REQUESTDONE);
+ reqev = (dns_requestevent_t *)event;
+ request = reqev->request;
+ result = eresult = reqev->result;
+ ctx = reqev->ev_arg;
+ REQUIRE(REQCTX_VALID(ctx));
+
+ isc_event_free(&event);
+
+ LOCK(&ctx->lock);
+
+ if (eresult == ISC_R_SUCCESS) {
+ result = dns_request_getresponse(request, ctx->event->rmessage,
+ ctx->parseoptions);
+ }
+
+ if (ctx->tsigkey != NULL)
+ dns_tsigkey_detach(&ctx->tsigkey);
+
+ if (ctx->canceled)
+ ctx->event->result = ISC_R_CANCELED;
+ else
+ ctx->event->result = result;
+ task = ctx->event->ev_sender;
+ ctx->event->ev_sender = ctx;
+ isc_task_sendanddetach(&task, ISC_EVENT_PTR(&ctx->event));
+
+ UNLOCK(&ctx->lock);
+}
+
+static void
+localrequest_done(isc_task_t *task, isc_event_t *event) {
+ reqarg_t *reqarg = event->ev_arg;
+ dns_clientreqevent_t *rev =(dns_clientreqevent_t *)event;
+
+ UNUSED(task);
+
+ REQUIRE(event->ev_type == DNS_EVENT_CLIENTREQDONE);
+
+ LOCK(&reqarg->lock);
+
+ reqarg->result = rev->result;
+ dns_client_destroyreqtrans(&reqarg->trans);
+ isc_event_free(&event);
+
+ if (!reqarg->canceled) {
+ UNLOCK(&reqarg->lock);
+
+ /* Exit from the internal event loop */
+ isc_app_ctxsuspend(reqarg->actx);
+ } else {
+ /*
+ * We have already exited from the loop (due to some
+ * unexpected event). Just clean the arg up.
+ */
+ UNLOCK(&reqarg->lock);
+ DESTROYLOCK(&reqarg->lock);
+ isc_mem_put(reqarg->client->mctx, reqarg, sizeof(*reqarg));
+ }
+}
+
+isc_result_t
+dns_client_request(dns_client_t *client, dns_message_t *qmessage,
+ dns_message_t *rmessage, isc_sockaddr_t *server,
+ unsigned int options, unsigned int parseoptions,
+ dns_tsec_t *tsec, unsigned int timeout,
+ unsigned int udptimeout, unsigned int udpretries)
+{
+ isc_appctx_t *actx;
+ reqarg_t *reqarg;
+ isc_result_t result;
+
+ REQUIRE(DNS_CLIENT_VALID(client));
+ REQUIRE(qmessage != NULL);
+ REQUIRE(rmessage != NULL);
+
+ if ((client->attributes & DNS_CLIENTATTR_OWNCTX) == 0 &&
+ (options & DNS_CLIENTREQOPT_ALLOWRUN) == 0) {
+ /*
+ * If the client is run under application's control, we need
+ * to create a new running (sub)environment for this
+ * particular resolution.
+ */
+ return (ISC_R_NOTIMPLEMENTED); /* XXXTBD */
+ } else
+ actx = client->actx;
+
+ reqarg = isc_mem_get(client->mctx, sizeof(*reqarg));
+ if (reqarg == NULL)
+ return (ISC_R_NOMEMORY);
+
+ result = isc_mutex_init(&reqarg->lock);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(client->mctx, reqarg, sizeof(*reqarg));
+ return (result);
+ }
+
+ reqarg->actx = actx;
+ reqarg->client = client;
+ reqarg->trans = NULL;
+ reqarg->canceled = false;
+
+ result = dns_client_startrequest(client, qmessage, rmessage, server,
+ options, parseoptions, tsec, timeout,
+ udptimeout, udpretries,
+ client->task, localrequest_done,
+ reqarg, &reqarg->trans);
+ if (result != ISC_R_SUCCESS) {
+ DESTROYLOCK(&reqarg->lock);
+ isc_mem_put(client->mctx, reqarg, sizeof(*reqarg));
+ return (result);
+ }
+
+ /*
+ * Start internal event loop. It blocks until the entire process
+ * is completed.
+ */
+ result = isc_app_ctxrun(actx);
+
+ LOCK(&reqarg->lock);
+ if (result == ISC_R_SUCCESS || result == ISC_R_SUSPEND)
+ result = reqarg->result;
+ if (reqarg->trans != NULL) {
+ /*
+ * Unusual termination (perhaps due to signal). We need some
+ * tricky cleanup process.
+ */
+ reqarg->canceled = true;
+ dns_client_cancelresolve(reqarg->trans);
+
+ UNLOCK(&reqarg->lock);
+
+ /* reqarg will be freed in the event handler. */
+ } else {
+ UNLOCK(&reqarg->lock);
+
+ DESTROYLOCK(&reqarg->lock);
+ isc_mem_put(client->mctx, reqarg, sizeof(*reqarg));
+ }
+
+ return (result);
+}
+
+isc_result_t
+dns_client_startrequest(dns_client_t *client, dns_message_t *qmessage,
+ dns_message_t *rmessage, isc_sockaddr_t *server,
+ unsigned int options, unsigned int parseoptions,
+ dns_tsec_t *tsec, unsigned int timeout,
+ unsigned int udptimeout, unsigned int udpretries,
+ isc_task_t *task, isc_taskaction_t action, void *arg,
+ dns_clientreqtrans_t **transp)
+{
+ isc_result_t result;
+ dns_view_t *view = NULL;
+ isc_task_t *tclone = NULL;
+ dns_clientreqevent_t *event = NULL;
+ reqctx_t *ctx = NULL;
+ dns_tsectype_t tsectype = dns_tsectype_none;
+ unsigned int reqoptions;
+
+ REQUIRE(DNS_CLIENT_VALID(client));
+ REQUIRE(qmessage != NULL);
+ REQUIRE(rmessage != NULL);
+ REQUIRE(transp != NULL && *transp == NULL);
+
+ if (tsec != NULL) {
+ tsectype = dns_tsec_gettype(tsec);
+ if (tsectype != dns_tsectype_tsig)
+ return (ISC_R_NOTIMPLEMENTED); /* XXX */
+ }
+
+ LOCK(&client->lock);
+ result = dns_viewlist_find(&client->viewlist, DNS_CLIENTVIEW_NAME,
+ qmessage->rdclass, &view);
+ UNLOCK(&client->lock);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ reqoptions = 0;
+ if ((options & DNS_CLIENTREQOPT_TCP) != 0)
+ reqoptions |= DNS_REQUESTOPT_TCP;
+
+ tclone = NULL;
+ isc_task_attach(task, &tclone);
+ event = (dns_clientreqevent_t *)
+ isc_event_allocate(client->mctx, tclone,
+ DNS_EVENT_CLIENTREQDONE,
+ action, arg, sizeof(*event));
+ if (event == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+
+ ctx = isc_mem_get(client->mctx, sizeof(*ctx));
+ if (ctx == NULL)
+ result = ISC_R_NOMEMORY;
+ else {
+ result = isc_mutex_init(&ctx->lock);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(client->mctx, ctx, sizeof(*ctx));
+ ctx = NULL;
+ }
+ }
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ ctx->client = client;
+ ISC_LINK_INIT(ctx, link);
+ ctx->parseoptions = parseoptions;
+ ctx->canceled = false;
+ ctx->event = event;
+ ctx->event->rmessage = rmessage;
+ ctx->tsigkey = NULL;
+ if (tsec != NULL)
+ dns_tsec_getkey(tsec, &ctx->tsigkey);
+
+ ctx->magic = REQCTX_MAGIC;
+
+ LOCK(&client->lock);
+ ISC_LIST_APPEND(client->reqctxs, ctx, link);
+ UNLOCK(&client->lock);
+
+ ctx->request = NULL;
+ result = dns_request_createvia3(view->requestmgr, qmessage, NULL,
+ server, reqoptions, ctx->tsigkey,
+ timeout, udptimeout, udpretries,
+ client->task, request_done, ctx,
+ &ctx->request);
+ if (result == ISC_R_SUCCESS) {
+ dns_view_detach(&view);
+ *transp = (dns_clientreqtrans_t *)ctx;
+ return (ISC_R_SUCCESS);
+ }
+
+ cleanup:
+ if (ctx != NULL) {
+ LOCK(&client->lock);
+ ISC_LIST_UNLINK(client->reqctxs, ctx, link);
+ UNLOCK(&client->lock);
+ DESTROYLOCK(&ctx->lock);
+ isc_mem_put(client->mctx, ctx, sizeof(*ctx));
+ }
+ if (event != NULL)
+ isc_event_free(ISC_EVENT_PTR(&event));
+ isc_task_detach(&tclone);
+ dns_view_detach(&view);
+
+ return (result);
+}
+
+void
+dns_client_cancelrequest(dns_clientreqtrans_t *trans) {
+ reqctx_t *ctx;
+
+ REQUIRE(trans != NULL);
+ ctx = (reqctx_t *)trans;
+ REQUIRE(REQCTX_VALID(ctx));
+
+ LOCK(&ctx->lock);
+
+ if (!ctx->canceled) {
+ ctx->canceled = true;
+ if (ctx->request != NULL)
+ dns_request_cancel(ctx->request);
+ }
+
+ UNLOCK(&ctx->lock);
+}
+
+void
+dns_client_destroyreqtrans(dns_clientreqtrans_t **transp) {
+ reqctx_t *ctx;
+ isc_mem_t *mctx;
+ dns_client_t *client;
+ bool need_destroyclient = false;
+
+ REQUIRE(transp != NULL);
+ ctx = (reqctx_t *)*transp;
+ REQUIRE(REQCTX_VALID(ctx));
+ client = ctx->client;
+ REQUIRE(DNS_CLIENT_VALID(client));
+ REQUIRE(ctx->event == NULL);
+ REQUIRE(ctx->request != NULL);
+
+ dns_request_destroy(&ctx->request);
+ mctx = client->mctx;
+
+ LOCK(&client->lock);
+
+ INSIST(ISC_LINK_LINKED(ctx, link));
+ ISC_LIST_UNLINK(client->reqctxs, ctx, link);
+
+ if (client->references == 0 && ISC_LIST_EMPTY(client->resctxs) &&
+ ISC_LIST_EMPTY(client->reqctxs) &&
+ ISC_LIST_EMPTY(client->updatectxs)) {
+ need_destroyclient = true;
+ }
+
+ UNLOCK(&client->lock);
+
+ DESTROYLOCK(&ctx->lock);
+ ctx->magic = 0;
+
+ isc_mem_put(mctx, ctx, sizeof(*ctx));
+
+ if (need_destroyclient)
+ destroyclient(&client);
+
+ *transp = NULL;
+}
+
+/*%
+ * Dynamic update routines
+ */
+static isc_result_t
+rcode2result(dns_rcode_t rcode) {
+ /* XXX: isn't there a similar function? */
+ switch (rcode) {
+ case dns_rcode_formerr:
+ return (DNS_R_FORMERR);
+ case dns_rcode_servfail:
+ return (DNS_R_SERVFAIL);
+ case dns_rcode_nxdomain:
+ return (DNS_R_NXDOMAIN);
+ case dns_rcode_notimp:
+ return (DNS_R_NOTIMP);
+ case dns_rcode_refused:
+ return (DNS_R_REFUSED);
+ case dns_rcode_yxdomain:
+ return (DNS_R_YXDOMAIN);
+ case dns_rcode_yxrrset:
+ return (DNS_R_YXRRSET);
+ case dns_rcode_nxrrset:
+ return (DNS_R_NXRRSET);
+ case dns_rcode_notauth:
+ return (DNS_R_NOTAUTH);
+ case dns_rcode_notzone:
+ return (DNS_R_NOTZONE);
+ case dns_rcode_badvers:
+ return (DNS_R_BADVERS);
+ }
+
+ return (ISC_R_FAILURE);
+}
+
+static void
+update_sendevent(updatectx_t *uctx, isc_result_t result) {
+ isc_task_t *task;
+
+ dns_message_destroy(&uctx->updatemsg);
+ if (uctx->tsigkey != NULL)
+ dns_tsigkey_detach(&uctx->tsigkey);
+ if (uctx->sig0key != NULL)
+ dst_key_free(&uctx->sig0key);
+
+ if (uctx->canceled)
+ uctx->event->result = ISC_R_CANCELED;
+ else
+ uctx->event->result = result;
+ uctx->event->state = uctx->state;
+ task = uctx->event->ev_sender;
+ uctx->event->ev_sender = uctx;
+ isc_task_sendanddetach(&task, ISC_EVENT_PTR(&uctx->event));
+}
+
+static void
+update_done(isc_task_t *task, isc_event_t *event) {
+ isc_result_t result;
+ dns_requestevent_t *reqev = NULL;
+ dns_request_t *request;
+ dns_message_t *answer = NULL;
+ updatectx_t *uctx = event->ev_arg;
+ dns_client_t *client;
+ unsigned int timeout, reqoptions;
+
+ UNUSED(task);
+
+ REQUIRE(event->ev_type == DNS_EVENT_REQUESTDONE);
+ reqev = (dns_requestevent_t *)event;
+ request = reqev->request;
+ REQUIRE(UCTX_VALID(uctx));
+ client = uctx->client;
+ REQUIRE(DNS_CLIENT_VALID(client));
+
+ result = reqev->result;
+ if (result != ISC_R_SUCCESS)
+ goto out;
+
+ result = dns_message_create(client->mctx, DNS_MESSAGE_INTENTPARSE,
+ &answer);
+ if (result != ISC_R_SUCCESS)
+ goto out;
+ uctx->state = dns_clientupdatestate_done;
+ result = dns_request_getresponse(request, answer,
+ DNS_MESSAGEPARSE_PRESERVEORDER);
+ if (result == ISC_R_SUCCESS && answer->rcode != dns_rcode_noerror)
+ result = rcode2result(answer->rcode);
+
+ out:
+ if (answer != NULL)
+ dns_message_destroy(&answer);
+ isc_event_free(&event);
+
+ LOCK(&uctx->lock);
+ uctx->currentserver = ISC_LIST_NEXT(uctx->currentserver, link);
+ dns_request_destroy(&uctx->updatereq);
+ /*
+ * Moving on to the next server shouldn't change the result
+ * for NXDOMAIN, YXDOMAIN, NXRRSET and YXRRSET as they
+ * indicate a prerequisite failure. REFUSED should also
+ * be consistent across all servers but often isn't as that
+ * is policy rather that zone content driven (slaves that
+ * aren't willing to forward should return NOTIMPL). NOTZONE
+ * indicates that we stuffed up the request construction so
+ * don't retry.
+ */
+ if (result != ISC_R_SUCCESS && result != DNS_R_NXDOMAIN &&
+ result != DNS_R_YXDOMAIN && result != DNS_R_YXRRSET &&
+ result != DNS_R_NXRRSET && result != DNS_R_NOTZONE &&
+ !uctx->canceled && uctx->currentserver != NULL)
+ {
+ dns_message_renderreset(uctx->updatemsg);
+ dns_message_settsigkey(uctx->updatemsg, NULL);
+
+ timeout = client->update_timeout / uctx->nservers;
+ if (timeout < MIN_UPDATE_TIMEOUT)
+ timeout = MIN_UPDATE_TIMEOUT;
+ reqoptions = 0;
+ if (uctx->want_tcp)
+ reqoptions |= DNS_REQUESTOPT_TCP;
+ result = dns_request_createvia3(uctx->view->requestmgr,
+ uctx->updatemsg,
+ NULL,
+ uctx->currentserver,
+ reqoptions,
+ uctx->tsigkey,
+ timeout,
+ client->update_udptimeout,
+ client->update_udpretries,
+ client->task,
+ update_done, uctx,
+ &uctx->updatereq);
+ UNLOCK(&uctx->lock);
+
+ if (result == ISC_R_SUCCESS) {
+ /* XXX: should we keep the 'done' state here? */
+ uctx->state = dns_clientupdatestate_sent;
+ return;
+ }
+ } else
+ UNLOCK(&uctx->lock);
+
+ update_sendevent(uctx, result);
+}
+
+static isc_result_t
+send_update(updatectx_t *uctx) {
+ isc_result_t result;
+ dns_name_t *name = NULL;
+ dns_rdataset_t *rdataset = NULL;
+ dns_client_t *client = uctx->client;
+ unsigned int timeout, reqoptions;
+
+ REQUIRE(uctx->zonename != NULL && uctx->currentserver != NULL);
+
+ result = dns_message_gettempname(uctx->updatemsg, &name);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ dns_name_init(name, NULL);
+ dns_name_clone(uctx->zonename, name);
+ result = dns_message_gettemprdataset(uctx->updatemsg, &rdataset);
+ if (result != ISC_R_SUCCESS) {
+ dns_message_puttempname(uctx->updatemsg, &name);
+ return (result);
+ }
+ dns_rdataset_makequestion(rdataset, uctx->rdclass, dns_rdatatype_soa);
+ ISC_LIST_INIT(name->list);
+ ISC_LIST_APPEND(name->list, rdataset, link);
+ dns_message_addname(uctx->updatemsg, name, DNS_SECTION_ZONE);
+ if (uctx->tsigkey == NULL && uctx->sig0key != NULL) {
+ result = dns_message_setsig0key(uctx->updatemsg,
+ uctx->sig0key);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+ timeout = client->update_timeout / uctx->nservers;
+ if (timeout < MIN_UPDATE_TIMEOUT)
+ timeout = MIN_UPDATE_TIMEOUT;
+ reqoptions = 0;
+ if (uctx->want_tcp)
+ reqoptions |= DNS_REQUESTOPT_TCP;
+ result = dns_request_createvia3(uctx->view->requestmgr,
+ uctx->updatemsg,
+ NULL, uctx->currentserver,
+ reqoptions, uctx->tsigkey, timeout,
+ client->update_udptimeout,
+ client->update_udpretries,
+ client->task, update_done, uctx,
+ &uctx->updatereq);
+ if (result == ISC_R_SUCCESS &&
+ uctx->state == dns_clientupdatestate_prepare) {
+ uctx->state = dns_clientupdatestate_sent;
+ }
+
+ return (result);
+}
+
+static void
+resolveaddr_done(isc_task_t *task, isc_event_t *event) {
+ isc_result_t result;
+ int family;
+ dns_rdatatype_t qtype;
+ dns_clientresevent_t *rev = (dns_clientresevent_t *)event;
+ dns_name_t *name;
+ dns_rdataset_t *rdataset;
+ updatectx_t *uctx;
+ bool completed = false;
+
+ UNUSED(task);
+
+ REQUIRE(event->ev_arg != NULL);
+ uctx = *(updatectx_t **)event->ev_arg;
+ REQUIRE(UCTX_VALID(uctx));
+
+ if (event->ev_arg == &uctx->bp4) {
+ family = AF_INET;
+ qtype = dns_rdatatype_a;
+ LOCK(&uctx->lock);
+ dns_client_destroyrestrans(&uctx->restrans);
+ UNLOCK(&uctx->lock);
+ } else {
+ INSIST(event->ev_arg == &uctx->bp6);
+ family = AF_INET6;
+ qtype = dns_rdatatype_aaaa;
+ LOCK(&uctx->lock);
+ dns_client_destroyrestrans(&uctx->restrans2);
+ UNLOCK(&uctx->lock);
+ }
+
+ result = rev->result;
+ if (result != ISC_R_SUCCESS)
+ goto done;
+
+ for (name = ISC_LIST_HEAD(rev->answerlist); name != NULL;
+ name = ISC_LIST_NEXT(name, link)) {
+ for (rdataset = ISC_LIST_HEAD(name->list);
+ rdataset != NULL;
+ rdataset = ISC_LIST_NEXT(rdataset, link)) {
+ if (!dns_rdataset_isassociated(rdataset))
+ continue;
+ if (rdataset->type != qtype)
+ continue;
+
+ for (result = dns_rdataset_first(rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(rdataset)) {
+ dns_rdata_t rdata;
+ dns_rdata_in_a_t rdata_a;
+ dns_rdata_in_aaaa_t rdata_aaaa;
+ isc_sockaddr_t *sa;
+
+ sa = isc_mem_get(uctx->client->mctx,
+ sizeof(*sa));
+ if (sa == NULL) {
+ /*
+ * If we fail to get a sockaddr,
+ we simply move forward with the
+ * addresses we've got so far.
+ */
+ goto done;
+ }
+
+ dns_rdata_init(&rdata);
+ switch (family) {
+ case AF_INET:
+ dns_rdataset_current(rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &rdata_a,
+ NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ isc_sockaddr_fromin(sa,
+ &rdata_a.in_addr,
+ 53);
+ dns_rdata_freestruct(&rdata_a);
+ break;
+ case AF_INET6:
+ dns_rdataset_current(rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &rdata_aaaa,
+ NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ isc_sockaddr_fromin6(sa,
+ &rdata_aaaa.in6_addr,
+ 53);
+ dns_rdata_freestruct(&rdata_aaaa);
+ break;
+ }
+
+ ISC_LINK_INIT(sa, link);
+ ISC_LIST_APPEND(uctx->servers, sa, link);
+ uctx->nservers++;
+ }
+ }
+ }
+
+ done:
+ dns_client_freeresanswer(uctx->client, &rev->answerlist);
+ isc_event_free(&event);
+
+ LOCK(&uctx->lock);
+ if (uctx->restrans == NULL && uctx->restrans2 == NULL)
+ completed = true;
+ UNLOCK(&uctx->lock);
+
+ if (completed) {
+ INSIST(uctx->currentserver == NULL);
+ uctx->currentserver = ISC_LIST_HEAD(uctx->servers);
+ if (uctx->currentserver != NULL && !uctx->canceled)
+ send_update(uctx);
+ else {
+ if (result == ISC_R_SUCCESS)
+ result = ISC_R_NOTFOUND;
+ update_sendevent(uctx, result);
+ }
+ }
+}
+
+static isc_result_t
+process_soa(updatectx_t *uctx, dns_rdataset_t *soaset, dns_name_t *soaname) {
+ isc_result_t result;
+ dns_rdata_t soarr = DNS_RDATA_INIT;
+ dns_rdata_soa_t soa;
+ dns_name_t primary;
+ unsigned int resoptions;
+
+ result = dns_rdataset_first(soaset);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ dns_rdata_init(&soarr);
+ dns_rdataset_current(soaset, &soarr);
+ result = dns_rdata_tostruct(&soarr, &soa, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ dns_name_init(&primary, NULL);
+ dns_name_clone(&soa.origin, &primary);
+
+ if (uctx->zonename == NULL) {
+ uctx->zonename = dns_fixedname_name(&uctx->zonefname);
+ result = dns_name_copy(soaname, uctx->zonename, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto out;
+ }
+
+ if (uctx->currentserver != NULL)
+ result = send_update(uctx);
+ else {
+ /*
+ * Get addresses of the primary server. We don't use the ADB
+ * feature so that we could avoid caching data.
+ */
+ LOCK(&uctx->lock);
+ uctx->bp4 = uctx;
+ resoptions = 0;
+ if (uctx->want_tcp)
+ resoptions |= DNS_CLIENTRESOPT_TCP;
+ result = dns_client_startresolve(uctx->client, &primary,
+ uctx->rdclass,
+ dns_rdatatype_a,
+ resoptions,
+ uctx->client->task,
+ resolveaddr_done, &uctx->bp4,
+ &uctx->restrans);
+ if (result == ISC_R_SUCCESS) {
+ uctx->bp6 = uctx;
+ result = dns_client_startresolve(uctx->client,
+ &primary,
+ uctx->rdclass,
+ dns_rdatatype_aaaa,
+ resoptions,
+ uctx->client->task,
+ resolveaddr_done,
+ &uctx->bp6,
+ &uctx->restrans2);
+ }
+ UNLOCK(&uctx->lock);
+ }
+
+ out:
+ dns_rdata_freestruct(&soa);
+
+ return (result);
+}
+
+static void
+receive_soa(isc_task_t *task, isc_event_t *event) {
+ dns_requestevent_t *reqev = NULL;
+ updatectx_t *uctx;
+ dns_client_t *client;
+ isc_result_t result, eresult;
+ dns_request_t *request;
+ dns_message_t *rcvmsg = NULL;
+ dns_section_t section;
+ dns_rdataset_t *soaset = NULL;
+ int pass = 0;
+ dns_name_t *name;
+ dns_message_t *soaquery = NULL;
+ isc_sockaddr_t *addr;
+ bool seencname = false;
+ bool droplabel = false;
+ dns_name_t tname;
+ unsigned int nlabels, reqoptions;
+
+ UNUSED(task);
+
+ REQUIRE(event->ev_type == DNS_EVENT_REQUESTDONE);
+ reqev = (dns_requestevent_t *)event;
+ request = reqev->request;
+ result = eresult = reqev->result;
+ POST(result);
+ uctx = reqev->ev_arg;
+ client = uctx->client;
+ soaquery = uctx->soaquery;
+ addr = uctx->currentserver;
+ INSIST(addr != NULL);
+
+ isc_event_free(&event);
+
+ if (eresult != ISC_R_SUCCESS) {
+ result = eresult;
+ goto out;
+ }
+
+ result = dns_message_create(uctx->client->mctx,
+ DNS_MESSAGE_INTENTPARSE, &rcvmsg);
+ if (result != ISC_R_SUCCESS)
+ goto out;
+ result = dns_request_getresponse(request, rcvmsg,
+ DNS_MESSAGEPARSE_PRESERVEORDER);
+
+ if (result == DNS_R_TSIGERRORSET) {
+ dns_request_t *newrequest = NULL;
+
+ /* Retry SOA request without TSIG */
+ dns_message_destroy(&rcvmsg);
+ dns_message_renderreset(uctx->soaquery);
+ reqoptions = 0;
+ if (uctx->want_tcp)
+ reqoptions |= DNS_REQUESTOPT_TCP;
+ result = dns_request_createvia3(uctx->view->requestmgr,
+ uctx->soaquery, NULL, addr,
+ reqoptions, NULL,
+ client->find_timeout * 20,
+ client->find_timeout, 3,
+ uctx->client->task,
+ receive_soa, uctx,
+ &newrequest);
+ if (result == ISC_R_SUCCESS) {
+ LOCK(&uctx->lock);
+ dns_request_destroy(&uctx->soareq);
+ uctx->soareq = newrequest;
+ UNLOCK(&uctx->lock);
+
+ return;
+ }
+ goto out;
+ }
+
+ section = DNS_SECTION_ANSWER;
+ POST(section);
+
+ if (rcvmsg->rcode != dns_rcode_noerror &&
+ rcvmsg->rcode != dns_rcode_nxdomain) {
+ result = rcode2result(rcvmsg->rcode);
+ goto out;
+ }
+
+ lookforsoa:
+ if (pass == 0)
+ section = DNS_SECTION_ANSWER;
+ else if (pass == 1)
+ section = DNS_SECTION_AUTHORITY;
+ else {
+ droplabel = true;
+ goto out;
+ }
+
+ result = dns_message_firstname(rcvmsg, section);
+ if (result != ISC_R_SUCCESS) {
+ pass++;
+ goto lookforsoa;
+ }
+ while (result == ISC_R_SUCCESS) {
+ name = NULL;
+ dns_message_currentname(rcvmsg, section, &name);
+ soaset = NULL;
+ result = dns_message_findtype(name, dns_rdatatype_soa, 0,
+ &soaset);
+ if (result == ISC_R_SUCCESS)
+ break;
+ if (section == DNS_SECTION_ANSWER) {
+ dns_rdataset_t *tset = NULL;
+ if (dns_message_findtype(name, dns_rdatatype_cname, 0,
+ &tset) == ISC_R_SUCCESS
+ ||
+ dns_message_findtype(name, dns_rdatatype_dname, 0,
+ &tset) == ISC_R_SUCCESS
+ )
+ {
+ seencname = true;
+ break;
+ }
+ }
+
+ result = dns_message_nextname(rcvmsg, section);
+ }
+
+ if (soaset == NULL && !seencname) {
+ pass++;
+ goto lookforsoa;
+ }
+
+ if (seencname) {
+ droplabel = true;
+ goto out;
+ }
+
+ result = process_soa(uctx, soaset, name);
+
+ out:
+ if (droplabel) {
+ result = dns_message_firstname(soaquery, DNS_SECTION_QUESTION);
+ INSIST(result == ISC_R_SUCCESS);
+ name = NULL;
+ dns_message_currentname(soaquery, DNS_SECTION_QUESTION, &name);
+ nlabels = dns_name_countlabels(name);
+ if (nlabels == 1)
+ result = DNS_R_SERVFAIL; /* is there a better error? */
+ else {
+ dns_name_init(&tname, NULL);
+ dns_name_getlabelsequence(name, 1, nlabels - 1,
+ &tname);
+ dns_name_clone(&tname, name);
+ dns_request_destroy(&request);
+ LOCK(&uctx->lock);
+ uctx->soareq = NULL;
+ UNLOCK(&uctx->lock);
+ dns_message_renderreset(soaquery);
+ dns_message_settsigkey(soaquery, NULL);
+ reqoptions = 0;
+ if (uctx->want_tcp)
+ reqoptions |= DNS_REQUESTOPT_TCP;
+ result = dns_request_createvia3(uctx->view->requestmgr,
+ soaquery, NULL,
+ uctx->currentserver,
+ reqoptions,
+ uctx->tsigkey,
+ client->find_timeout *
+ 20,
+ client->find_timeout,
+ 3, client->task,
+ receive_soa, uctx,
+ &uctx->soareq);
+ }
+ }
+
+ if (!droplabel || result != ISC_R_SUCCESS) {
+ dns_message_destroy(&uctx->soaquery);
+ LOCK(&uctx->lock);
+ dns_request_destroy(&uctx->soareq);
+ UNLOCK(&uctx->lock);
+ }
+
+ if (rcvmsg != NULL)
+ dns_message_destroy(&rcvmsg);
+
+ if (result != ISC_R_SUCCESS)
+ update_sendevent(uctx, result);
+}
+
+static isc_result_t
+request_soa(updatectx_t *uctx) {
+ isc_result_t result;
+ dns_message_t *soaquery = uctx->soaquery;
+ dns_name_t *name = NULL;
+ dns_rdataset_t *rdataset = NULL;
+ unsigned int reqoptions;
+
+ if (soaquery == NULL) {
+ result = dns_message_create(uctx->client->mctx,
+ DNS_MESSAGE_INTENTRENDER,
+ &soaquery);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+ soaquery->flags |= DNS_MESSAGEFLAG_RD;
+ result = dns_message_gettempname(soaquery, &name);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+ result = dns_message_gettemprdataset(soaquery, &rdataset);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+ dns_rdataset_makequestion(rdataset, uctx->rdclass, dns_rdatatype_soa);
+ dns_name_clone(uctx->firstname, name);
+ ISC_LIST_APPEND(name->list, rdataset, link);
+ dns_message_addname(soaquery, name, DNS_SECTION_QUESTION);
+ rdataset = NULL;
+ name = NULL;
+ reqoptions = 0;
+ if (uctx->want_tcp)
+ reqoptions |= DNS_REQUESTOPT_TCP;
+
+ result = dns_request_createvia3(uctx->view->requestmgr,
+ soaquery, NULL, uctx->currentserver,
+ reqoptions, uctx->tsigkey,
+ uctx->client->find_timeout * 20,
+ uctx->client->find_timeout, 3,
+ uctx->client->task, receive_soa, uctx,
+ &uctx->soareq);
+ if (result == ISC_R_SUCCESS) {
+ uctx->soaquery = soaquery;
+ return (ISC_R_SUCCESS);
+ }
+
+ fail:
+ if (rdataset != NULL) {
+ ISC_LIST_UNLINK(name->list, rdataset, link); /* for safety */
+ dns_message_puttemprdataset(soaquery, &rdataset);
+ }
+ if (name != NULL)
+ dns_message_puttempname(soaquery, &name);
+ dns_message_destroy(&soaquery);
+
+ return (result);
+}
+
+static void
+resolvesoa_done(isc_task_t *task, isc_event_t *event) {
+ dns_clientresevent_t *rev = (dns_clientresevent_t *)event;
+ updatectx_t *uctx;
+ dns_name_t *name, tname;
+ dns_rdataset_t *rdataset = NULL;
+ isc_result_t result = rev->result;
+ unsigned int nlabels, resoptions;
+
+ UNUSED(task);
+
+ uctx = event->ev_arg;
+ REQUIRE(UCTX_VALID(uctx));
+
+ LOCK(&uctx->lock);
+ dns_client_destroyrestrans(&uctx->restrans);
+ UNLOCK(&uctx->lock);
+
+ uctx = event->ev_arg;
+ if (result != ISC_R_SUCCESS &&
+ result != DNS_R_NCACHENXDOMAIN &&
+ result != DNS_R_NCACHENXRRSET) {
+ /* XXX: what about DNSSEC failure? */
+ goto out;
+ }
+
+ for (name = ISC_LIST_HEAD(rev->answerlist); name != NULL;
+ name = ISC_LIST_NEXT(name, link)) {
+ for (rdataset = ISC_LIST_HEAD(name->list);
+ rdataset != NULL;
+ rdataset = ISC_LIST_NEXT(rdataset, link)) {
+ if (dns_rdataset_isassociated(rdataset) &&
+ rdataset->type == dns_rdatatype_soa)
+ break;
+ }
+ }
+
+ if (rdataset == NULL) {
+ /* Drop one label and retry resolution. */
+ nlabels = dns_name_countlabels(&uctx->soaqname);
+ if (nlabels == 1) {
+ result = DNS_R_SERVFAIL; /* is there a better error? */
+ goto out;
+ }
+ dns_name_init(&tname, NULL);
+ dns_name_getlabelsequence(&uctx->soaqname, 1, nlabels - 1,
+ &tname);
+ dns_name_clone(&tname, &uctx->soaqname);
+ resoptions = 0;
+ if (uctx->want_tcp)
+ resoptions |= DNS_CLIENTRESOPT_TCP;
+
+ result = dns_client_startresolve(uctx->client, &uctx->soaqname,
+ uctx->rdclass,
+ dns_rdatatype_soa,
+ resoptions,
+ uctx->client->task,
+ resolvesoa_done, uctx,
+ &uctx->restrans);
+ } else
+ result = process_soa(uctx, rdataset, &uctx->soaqname);
+
+ out:
+ dns_client_freeresanswer(uctx->client, &rev->answerlist);
+ isc_event_free(&event);
+
+ if (result != ISC_R_SUCCESS)
+ update_sendevent(uctx, result);
+}
+
+static isc_result_t
+copy_name(isc_mem_t *mctx, dns_message_t *msg, dns_name_t *name,
+ dns_name_t **newnamep)
+{
+ isc_result_t result;
+ dns_name_t *newname = NULL;
+ isc_region_t r;
+ isc_buffer_t *namebuf = NULL, *rdatabuf = NULL;
+ dns_rdatalist_t *rdatalist;
+ dns_rdataset_t *rdataset, *newrdataset;
+ dns_rdata_t rdata = DNS_RDATA_INIT, *newrdata;
+
+ result = dns_message_gettempname(msg, &newname);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ result = isc_buffer_allocate(mctx, &namebuf, DNS_NAME_MAXWIRE);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+ dns_name_init(newname, NULL);
+ dns_name_setbuffer(newname, namebuf);
+ dns_message_takebuffer(msg, &namebuf);
+ result = dns_name_copy(name, newname, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+
+ for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL;
+ rdataset = ISC_LIST_NEXT(rdataset, link)) {
+ rdatalist = NULL;
+ result = dns_message_gettemprdatalist(msg, &rdatalist);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+ dns_rdatalist_init(rdatalist);
+ rdatalist->type = rdataset->type;
+ rdatalist->rdclass = rdataset->rdclass;
+ rdatalist->covers = rdataset->covers;
+ rdatalist->ttl = rdataset->ttl;
+
+ result = dns_rdataset_first(rdataset);
+ while (result == ISC_R_SUCCESS) {
+ dns_rdata_reset(&rdata);
+ dns_rdataset_current(rdataset, &rdata);
+
+ newrdata = NULL;
+ result = dns_message_gettemprdata(msg, &newrdata);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+ dns_rdata_toregion(&rdata, &r);
+ rdatabuf = NULL;
+ result = isc_buffer_allocate(mctx, &rdatabuf,
+ r.length);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+ isc_buffer_putmem(rdatabuf, r.base, r.length);
+ isc_buffer_usedregion(rdatabuf, &r);
+ dns_rdata_init(newrdata);
+ dns_rdata_fromregion(newrdata, rdata.rdclass,
+ rdata.type, &r);
+ newrdata->flags = rdata.flags;
+
+ ISC_LIST_APPEND(rdatalist->rdata, newrdata, link);
+ dns_message_takebuffer(msg, &rdatabuf);
+
+ result = dns_rdataset_next(rdataset);
+ }
+
+ newrdataset = NULL;
+ result = dns_message_gettemprdataset(msg, &newrdataset);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+ dns_rdatalist_tordataset(rdatalist, newrdataset);
+
+ ISC_LIST_APPEND(newname->list, newrdataset, link);
+ }
+
+ *newnamep = newname;
+
+ return (ISC_R_SUCCESS);
+
+ fail:
+ dns_message_puttempname(msg, &newname);
+
+ return (result);
+
+}
+
+static void
+internal_update_callback(isc_task_t *task, isc_event_t *event) {
+ updatearg_t *uarg = event->ev_arg;
+ dns_clientupdateevent_t *uev = (dns_clientupdateevent_t *)event;
+
+ UNUSED(task);
+
+ LOCK(&uarg->lock);
+
+ uarg->result = uev->result;
+
+ dns_client_destroyupdatetrans(&uarg->trans);
+ isc_event_free(&event);
+
+ if (!uarg->canceled) {
+ UNLOCK(&uarg->lock);
+
+ /* Exit from the internal event loop */
+ isc_app_ctxsuspend(uarg->actx);
+ } else {
+ /*
+ * We have already exited from the loop (due to some
+ * unexpected event). Just clean the arg up.
+ */
+ UNLOCK(&uarg->lock);
+ DESTROYLOCK(&uarg->lock);
+ isc_mem_put(uarg->client->mctx, uarg, sizeof(*uarg));
+ }
+}
+
+isc_result_t
+dns_client_update(dns_client_t *client, dns_rdataclass_t rdclass,
+ dns_name_t *zonename, dns_namelist_t *prerequisites,
+ dns_namelist_t *updates, isc_sockaddrlist_t *servers,
+ dns_tsec_t *tsec, unsigned int options)
+{
+ isc_result_t result;
+ isc_appctx_t *actx;
+ updatearg_t *uarg;
+
+ REQUIRE(DNS_CLIENT_VALID(client));
+
+ if ((client->attributes & DNS_CLIENTATTR_OWNCTX) == 0 &&
+ (options & DNS_CLIENTUPDOPT_ALLOWRUN) == 0) {
+ /*
+ * If the client is run under application's control, we need
+ * to create a new running (sub)environment for this
+ * particular update.
+ */
+ return (ISC_R_NOTIMPLEMENTED); /* XXXTBD */
+ } else
+ actx = client->actx;
+
+ uarg = isc_mem_get(client->mctx, sizeof(*uarg));
+ if (uarg == NULL)
+ return (ISC_R_NOMEMORY);
+
+ result = isc_mutex_init(&uarg->lock);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(client->mctx, uarg, sizeof(*uarg));
+ return (result);
+ }
+
+ uarg->actx = actx;
+ uarg->client = client;
+ uarg->result = ISC_R_FAILURE;
+ uarg->trans = NULL;
+ uarg->canceled = false;
+
+ result = dns_client_startupdate(client, rdclass, zonename,
+ prerequisites, updates, servers,
+ tsec, options, client->task,
+ internal_update_callback, uarg,
+ &uarg->trans);
+ if (result != ISC_R_SUCCESS) {
+ DESTROYLOCK(&uarg->lock);
+ isc_mem_put(client->mctx, uarg, sizeof(*uarg));
+ return (result);
+ }
+
+ /*
+ * Start internal event loop. It blocks until the entire process
+ * is completed.
+ */
+ result = isc_app_ctxrun(actx);
+
+ LOCK(&uarg->lock);
+ if (result == ISC_R_SUCCESS || result == ISC_R_SUSPEND)
+ result = uarg->result;
+
+ if (uarg->trans != NULL) {
+ /*
+ * Unusual termination (perhaps due to signal). We need some
+ * tricky cleanup process.
+ */
+ uarg->canceled = true;
+ dns_client_cancelupdate(uarg->trans);
+
+ UNLOCK(&uarg->lock);
+
+ /* uarg will be freed in the event handler. */
+ } else {
+ UNLOCK(&uarg->lock);
+
+ DESTROYLOCK(&uarg->lock);
+ isc_mem_put(client->mctx, uarg, sizeof(*uarg));
+ }
+
+ return (result);
+}
+
+static void
+startupdate(isc_task_t *task, isc_event_t *event) {
+ updatectx_t *uctx;
+ isc_result_t result;
+ unsigned int resoptions;
+
+ REQUIRE(event != NULL);
+
+ UNUSED(task);
+
+ uctx = event->ev_arg;
+
+ if (uctx->zonename != NULL && uctx->currentserver != NULL) {
+ result = send_update(uctx);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+ } else if (uctx->currentserver != NULL) {
+ result = request_soa(uctx);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+ } else {
+ resoptions = 0;
+ if (uctx->want_tcp)
+ resoptions |= DNS_CLIENTRESOPT_TCP;
+ dns_name_clone(uctx->firstname, &uctx->soaqname);
+ result = dns_client_startresolve(uctx->client, &uctx->soaqname,
+ uctx->rdclass,
+ dns_rdatatype_soa, resoptions,
+ uctx->client->task,
+ resolvesoa_done, uctx,
+ &uctx->restrans);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+ }
+
+ isc_event_free(&event);
+
+fail:
+ if (result != ISC_R_SUCCESS)
+ update_sendevent(uctx, result);
+}
+
+isc_result_t
+dns_client_startupdate(dns_client_t *client, dns_rdataclass_t rdclass,
+ dns_name_t *zonename, dns_namelist_t *prerequisites,
+ dns_namelist_t *updates, isc_sockaddrlist_t *servers,
+ dns_tsec_t *tsec, unsigned int options,
+ isc_task_t *task, isc_taskaction_t action, void *arg,
+ dns_clientupdatetrans_t **transp)
+{
+ dns_view_t *view = NULL;
+ isc_result_t result;
+ dns_name_t *name, *newname;
+ updatectx_t *uctx;
+ isc_task_t *tclone = NULL;
+ dns_section_t section = DNS_SECTION_UPDATE;
+ isc_sockaddr_t *server, *sa = NULL;
+ dns_tsectype_t tsectype = dns_tsectype_none;
+ bool want_tcp;
+
+ UNUSED(options);
+
+ REQUIRE(DNS_CLIENT_VALID(client));
+ REQUIRE(transp != NULL && *transp == NULL);
+ REQUIRE(updates != NULL);
+ REQUIRE(task != NULL);
+
+ if (tsec != NULL) {
+ tsectype = dns_tsec_gettype(tsec);
+ if (tsectype != dns_tsectype_tsig)
+ return (ISC_R_NOTIMPLEMENTED); /* XXX */
+ }
+
+ LOCK(&client->lock);
+ result = dns_viewlist_find(&client->viewlist, DNS_CLIENTVIEW_NAME,
+ rdclass, &view);
+ UNLOCK(&client->lock);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ want_tcp = (options & DNS_CLIENTUPDOPT_TCP);
+
+ /*
+ * Create a context and prepare some resources.
+ */
+
+ uctx = isc_mem_get(client->mctx, sizeof(*uctx));
+ if (uctx == NULL) {
+ dns_view_detach(&view);
+ return (ISC_R_NOMEMORY);
+ }
+
+ result = isc_mutex_init(&uctx->lock);
+ if (result != ISC_R_SUCCESS) {
+ dns_view_detach(&view);
+ isc_mem_put(client->mctx, uctx, sizeof(*uctx));
+ return (ISC_R_NOMEMORY);
+ }
+
+ tclone = NULL;
+ isc_task_attach(task, &tclone);
+ uctx->client = client;
+ ISC_LINK_INIT(uctx, link);
+ uctx->state = dns_clientupdatestate_prepare;
+ uctx->view = view;
+ uctx->rdclass = rdclass;
+ uctx->canceled = false;
+ uctx->updatemsg = NULL;
+ uctx->soaquery = NULL;
+ uctx->updatereq = NULL;
+ uctx->restrans = NULL;
+ uctx->restrans2 = NULL;
+ uctx->bp4 = NULL;
+ uctx->bp6 = NULL;
+ uctx->soareq = NULL;
+ uctx->event = NULL;
+ uctx->tsigkey = NULL;
+ uctx->sig0key = NULL;
+ uctx->zonename = NULL;
+ uctx->want_tcp = want_tcp;
+ dns_name_init(&uctx->soaqname, NULL);
+ ISC_LIST_INIT(uctx->servers);
+ uctx->nservers = 0;
+ uctx->currentserver = NULL;
+ dns_fixedname_init(&uctx->zonefname);
+ if (tsec != NULL)
+ dns_tsec_getkey(tsec, &uctx->tsigkey);
+ uctx->event = (dns_clientupdateevent_t *)
+ isc_event_allocate(client->mctx, tclone, DNS_EVENT_UPDATEDONE,
+ action, arg, sizeof(*uctx->event));
+ if (uctx->event == NULL)
+ goto fail;
+ if (zonename != NULL) {
+ uctx->zonename = dns_fixedname_name(&uctx->zonefname);
+ result = dns_name_copy(zonename, uctx->zonename, NULL);
+ }
+ if (servers != NULL) {
+ for (server = ISC_LIST_HEAD(*servers);
+ server != NULL;
+ server = ISC_LIST_NEXT(server, link)) {
+ sa = isc_mem_get(client->mctx, sizeof(*sa));
+ if (sa == NULL)
+ goto fail;
+ sa->type = server->type;
+ sa->length = server->length;
+ ISC_LINK_INIT(sa, link);
+ ISC_LIST_APPEND(uctx->servers, sa, link);
+ if (uctx->currentserver == NULL)
+ uctx->currentserver = sa;
+ uctx->nservers++;
+ }
+ }
+
+ /* Make update message */
+ result = dns_message_create(client->mctx, DNS_MESSAGE_INTENTRENDER,
+ &uctx->updatemsg);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+ uctx->updatemsg->opcode = dns_opcode_update;
+
+ if (prerequisites != NULL) {
+ for (name = ISC_LIST_HEAD(*prerequisites); name != NULL;
+ name = ISC_LIST_NEXT(name, link)) {
+ newname = NULL;
+ result = copy_name(client->mctx, uctx->updatemsg,
+ name, &newname);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+ dns_message_addname(uctx->updatemsg, newname,
+ DNS_SECTION_PREREQUISITE);
+ }
+ }
+
+ for (name = ISC_LIST_HEAD(*updates); name != NULL;
+ name = ISC_LIST_NEXT(name, link)) {
+ newname = NULL;
+ result = copy_name(client->mctx, uctx->updatemsg, name,
+ &newname);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+ dns_message_addname(uctx->updatemsg, newname,
+ DNS_SECTION_UPDATE);
+ }
+
+ uctx->firstname = NULL;
+ result = dns_message_firstname(uctx->updatemsg, section);
+ if (result == ISC_R_NOMORE) {
+ section = DNS_SECTION_PREREQUISITE;
+ result = dns_message_firstname(uctx->updatemsg, section);
+ }
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+ dns_message_currentname(uctx->updatemsg, section, &uctx->firstname);
+
+ uctx->magic = UCTX_MAGIC;
+
+ LOCK(&client->lock);
+ ISC_LIST_APPEND(client->updatectxs, uctx, link);
+ UNLOCK(&client->lock);
+
+ *transp = (dns_clientupdatetrans_t *)uctx;
+ result = isc_app_ctxonrun(client->actx, client->mctx, client->task,
+ startupdate, uctx);
+ if (result == ISC_R_ALREADYRUNNING) {
+ isc_event_t *event;
+ event = isc_event_allocate(client->mctx, dns_client_startupdate,
+ DNS_EVENT_STARTUPDATE, startupdate,
+ uctx, sizeof(*event));
+ if (event != NULL) {
+ result = ISC_R_SUCCESS;
+ isc_task_send(task, &event);
+ } else
+ result = ISC_R_NOMEMORY;
+ }
+ if (result == ISC_R_SUCCESS)
+ return (result);
+ *transp = NULL;
+
+ fail:
+ if (ISC_LINK_LINKED(uctx, link)) {
+ LOCK(&client->lock);
+ ISC_LIST_UNLINK(client->updatectxs, uctx, link);
+ UNLOCK(&client->lock);
+ }
+ if (uctx->updatemsg != NULL)
+ dns_message_destroy(&uctx->updatemsg);
+ while ((sa = ISC_LIST_HEAD(uctx->servers)) != NULL) {
+ ISC_LIST_UNLINK(uctx->servers, sa, link);
+ isc_mem_put(client->mctx, sa, sizeof(*sa));
+ }
+ if (uctx->event != NULL)
+ isc_event_free(ISC_EVENT_PTR(&uctx->event));
+ if (uctx->tsigkey != NULL)
+ dns_tsigkey_detach(&uctx->tsigkey);
+ isc_task_detach(&tclone);
+ DESTROYLOCK(&uctx->lock);
+ uctx->magic = 0;
+ isc_mem_put(client->mctx, uctx, sizeof(*uctx));
+ dns_view_detach(&view);
+
+ return (result);
+}
+
+void
+dns_client_cancelupdate(dns_clientupdatetrans_t *trans) {
+ updatectx_t *uctx;
+
+ REQUIRE(trans != NULL);
+ uctx = (updatectx_t *)trans;
+ REQUIRE(UCTX_VALID(uctx));
+
+ LOCK(&uctx->lock);
+
+ if (!uctx->canceled) {
+ uctx->canceled = true;
+ if (uctx->updatereq != NULL)
+ dns_request_cancel(uctx->updatereq);
+ if (uctx->soareq != NULL)
+ dns_request_cancel(uctx->soareq);
+ if (uctx->restrans != NULL)
+ dns_client_cancelresolve(uctx->restrans);
+ if (uctx->restrans2 != NULL)
+ dns_client_cancelresolve(uctx->restrans2);
+ }
+
+ UNLOCK(&uctx->lock);
+}
+
+void
+dns_client_destroyupdatetrans(dns_clientupdatetrans_t **transp) {
+ updatectx_t *uctx;
+ isc_mem_t *mctx;
+ dns_client_t *client;
+ bool need_destroyclient = false;
+ isc_sockaddr_t *sa;
+
+ REQUIRE(transp != NULL);
+ uctx = (updatectx_t *)*transp;
+ REQUIRE(UCTX_VALID(uctx));
+ client = uctx->client;
+ REQUIRE(DNS_CLIENT_VALID(client));
+ REQUIRE(uctx->updatereq == NULL && uctx->updatemsg == NULL &&
+ uctx->soareq == NULL && uctx->soaquery == NULL &&
+ uctx->event == NULL && uctx->tsigkey == NULL &&
+ uctx->sig0key == NULL);
+
+ mctx = client->mctx;
+ dns_view_detach(&uctx->view);
+ while ((sa = ISC_LIST_HEAD(uctx->servers)) != NULL) {
+ ISC_LIST_UNLINK(uctx->servers, sa, link);
+ isc_mem_put(mctx, sa, sizeof(*sa));
+ }
+
+ LOCK(&client->lock);
+
+ INSIST(ISC_LINK_LINKED(uctx, link));
+ ISC_LIST_UNLINK(client->updatectxs, uctx, link);
+
+ if (client->references == 0 && ISC_LIST_EMPTY(client->resctxs) &&
+ ISC_LIST_EMPTY(client->reqctxs) &&
+ ISC_LIST_EMPTY(client->updatectxs))
+ need_destroyclient = true;
+
+ UNLOCK(&client->lock);
+
+ DESTROYLOCK(&uctx->lock);
+ uctx->magic = 0;
+
+ isc_mem_put(mctx, uctx, sizeof(*uctx));
+
+ if (need_destroyclient)
+ destroyclient(&client);
+
+ *transp = NULL;
+}
+
+isc_mem_t *
+dns_client_mctx(dns_client_t *client) {
+
+ REQUIRE(DNS_CLIENT_VALID(client));
+ return (client->mctx);
+}
+
+typedef struct {
+ isc_buffer_t buffer;
+ dns_rdataset_t rdataset;
+ dns_rdatalist_t rdatalist;
+ dns_rdata_t rdata;
+ size_t size;
+ isc_mem_t * mctx;
+ unsigned char data[FLEXIBLE_ARRAY_MEMBER];
+} dns_client_updaterec_t;
+
+isc_result_t
+dns_client_updaterec(dns_client_updateop_t op, dns_name_t *owner,
+ dns_rdatatype_t type, dns_rdata_t *source,
+ dns_ttl_t ttl, dns_name_t *target,
+ dns_rdataset_t *rdataset, dns_rdatalist_t *rdatalist,
+ dns_rdata_t *rdata, isc_mem_t *mctx)
+{
+ dns_client_updaterec_t *updaterec = NULL;
+ size_t size = offsetof(dns_client_updaterec_t, data);
+
+ REQUIRE(op < updateop_max);
+ REQUIRE(owner != NULL);
+ REQUIRE((rdataset != NULL && rdatalist != NULL && rdata != NULL) ||
+ (rdataset == NULL && rdatalist == NULL && rdata == NULL &&
+ mctx != NULL));
+ if (op == updateop_add)
+ REQUIRE(source != NULL);
+ if (source != NULL) {
+ REQUIRE(source->type == type);
+ REQUIRE(op == updateop_add || op == updateop_delete ||
+ op == updateop_exist);
+ }
+
+ size += owner->length;
+ if (source != NULL)
+ size += source->length;
+
+ if (rdataset == NULL) {
+ updaterec = isc_mem_get(mctx, size);
+ if (updaterec == NULL)
+ return (ISC_R_NOMEMORY);
+ rdataset = &updaterec->rdataset;
+ rdatalist = &updaterec->rdatalist;
+ rdata = &updaterec->rdata;
+ dns_rdataset_init(rdataset);
+ dns_rdatalist_init(&updaterec->rdatalist);
+ dns_rdata_init(&updaterec->rdata);
+ isc_buffer_init(&updaterec->buffer, updaterec->data,
+ (unsigned int)
+ (size -
+ offsetof(dns_client_updaterec_t, data)));
+ dns_name_copy(owner, target, &updaterec->buffer);
+ if (source != NULL) {
+ isc_region_t r;
+ dns_rdata_clone(source, rdata);
+ dns_rdata_toregion(rdata, &r);
+ rdata->data = isc_buffer_used(&updaterec->buffer);
+ isc_buffer_copyregion(&updaterec->buffer, &r);
+ }
+ updaterec->mctx = NULL;
+ isc_mem_attach(mctx, &updaterec->mctx);
+ } else if (source != NULL)
+ dns_rdata_clone(source, rdata);
+
+ switch (op) {
+ case updateop_add:
+ break;
+ case updateop_delete:
+ if (source != NULL) {
+ ttl = 0;
+ dns_rdata_makedelete(rdata);
+ } else
+ dns_rdata_deleterrset(rdata, type);
+ break;
+ case updateop_notexist:
+ dns_rdata_notexist(rdata, type);
+ break;
+ case updateop_exist:
+ if (source == NULL) {
+ ttl = 0;
+ dns_rdata_exists(rdata, type);
+ }
+ case updateop_none:
+ break;
+ default:
+ INSIST(0);
+ }
+
+ rdatalist->type = rdata->type;
+ rdatalist->rdclass = rdata->rdclass;
+ if (source != NULL) {
+ rdatalist->covers = dns_rdata_covers(rdata);
+ rdatalist->ttl = ttl;
+ }
+ ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
+ dns_rdatalist_tordataset(rdatalist, rdataset);
+ ISC_LIST_APPEND(target->list, rdataset, link);
+ if (updaterec != NULL) {
+ target->attributes |= DNS_NAMEATTR_HASUPDATEREC;
+ dns_name_setbuffer(target, &updaterec->buffer);
+ }
+ if (op == updateop_add || op == updateop_delete)
+ target->attributes |= DNS_NAMEATTR_UPDATE;
+ else
+ target->attributes |= DNS_NAMEATTR_PREREQUISITE;
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_client_freeupdate(dns_name_t **namep) {
+ dns_client_updaterec_t *updaterec;
+ dns_rdatalist_t *rdatalist;
+ dns_rdataset_t *rdataset;
+ dns_rdata_t *rdata;
+ dns_name_t *name;
+
+ REQUIRE(namep != NULL && *namep != NULL);
+
+ name = *namep;
+ for (rdataset = ISC_LIST_HEAD(name->list);
+ rdataset != NULL;
+ rdataset = ISC_LIST_HEAD(name->list)) {
+ ISC_LIST_UNLINK(name->list, rdataset, link);
+ rdatalist = NULL;
+ dns_rdatalist_fromrdataset(rdataset, &rdatalist);
+ if (rdatalist == NULL) {
+ dns_rdataset_disassociate(rdataset);
+ continue;
+ }
+ for (rdata = ISC_LIST_HEAD(rdatalist->rdata);
+ rdata != NULL;
+ rdata = ISC_LIST_HEAD(rdatalist->rdata))
+ ISC_LIST_UNLINK(rdatalist->rdata, rdata, link);
+ dns_rdataset_disassociate(rdataset);
+ }
+
+ if ((name->attributes & DNS_NAMEATTR_HASUPDATEREC) != 0) {
+ updaterec = (dns_client_updaterec_t *)name->buffer;
+ INSIST(updaterec != NULL);
+ isc_mem_putanddetach(&updaterec->mctx, updaterec,
+ updaterec->size);
+ *namep = NULL;
+ }
+}
diff --git a/lib/dns/clientinfo.c b/lib/dns/clientinfo.c
new file mode 100644
index 0000000..6d025d9
--- /dev/null
+++ b/lib/dns/clientinfo.c
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include "config.h"
+
+#include <dns/clientinfo.h>
+
+void
+dns_clientinfomethods_init(dns_clientinfomethods_t *methods,
+ dns_clientinfo_sourceip_t sourceip)
+{
+ methods->version = DNS_CLIENTINFOMETHODS_VERSION;
+ methods->age = DNS_CLIENTINFOMETHODS_AGE;
+ methods->sourceip = sourceip;
+}
+
+void
+dns_clientinfo_init(dns_clientinfo_t *ci, void *data, void *versionp) {
+ ci->version = DNS_CLIENTINFO_VERSION;
+ ci->data = data;
+ ci->dbversion = versionp;
+}
diff --git a/lib/dns/compress.c b/lib/dns/compress.c
new file mode 100644
index 0000000..ec97235
--- /dev/null
+++ b/lib/dns/compress.c
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#define DNS_NAME_USEINLINE 1
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.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)
+
+#define TABLE_READY \
+ do { \
+ unsigned int i; \
+ \
+ if ((cctx->allowed & DNS_COMPRESS_READY) == 0) { \
+ cctx->allowed |= DNS_COMPRESS_READY; \
+ for (i = 0; i < DNS_COMPRESS_TABLESIZE; i++) \
+ cctx->table[i] = NULL; \
+ } \
+ } while (0)
+
+/***
+ *** Compression
+ ***/
+
+isc_result_t
+dns_compress_init(dns_compress_t *cctx, int edns, isc_mem_t *mctx) {
+ REQUIRE(cctx != NULL);
+ REQUIRE(mctx != NULL); /* See: rdataset.c:towiresorted(). */
+
+ cctx->edns = edns;
+ cctx->mctx = mctx;
+ cctx->count = 0;
+ cctx->allowed = DNS_COMPRESS_ENABLED;
+ 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));
+
+ if ((cctx->allowed & DNS_COMPRESS_READY) != 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->offset & 0x8000) != 0)
+ isc_mem_put(cctx->mctx, node->r.base,
+ node->r.length);
+ if (node->count < DNS_COMPRESS_INITIALNODES)
+ continue;
+ isc_mem_put(cctx->mctx, node, sizeof(*node));
+ }
+ }
+ }
+ cctx->magic = 0;
+ 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_disable(dns_compress_t *cctx) {
+ REQUIRE(VALID_CCTX(cctx));
+ cctx->allowed &= ~DNS_COMPRESS_ENABLED;
+}
+
+void
+dns_compress_setsensitive(dns_compress_t *cctx, bool sensitive) {
+ REQUIRE(VALID_CCTX(cctx));
+
+ if (sensitive)
+ cctx->allowed |= DNS_COMPRESS_CASESENSITIVE;
+ else
+ cctx->allowed &= ~DNS_COMPRESS_CASESENSITIVE;
+}
+
+bool
+dns_compress_getsensitive(dns_compress_t *cctx) {
+ REQUIRE(VALID_CCTX(cctx));
+
+ return (cctx->allowed & DNS_COMPRESS_CASESENSITIVE);
+}
+
+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 true. prefix, suffix and offset are updated.
+ * If no match is found return false.
+ */
+bool
+dns_compress_findglobal(dns_compress_t *cctx, const dns_name_t *name,
+ dns_name_t *prefix, 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) == true);
+ REQUIRE(offset != NULL);
+
+ if ((cctx->allowed & DNS_COMPRESS_ENABLED) == 0)
+ return (false);
+
+ TABLE_READY;
+
+ if (cctx->count == 0)
+ return (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, 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 (false);
+
+ if (n == 0)
+ dns_name_reset(prefix);
+ else
+ dns_name_getlabelsequence(name, 0, n, prefix);
+
+ *offset = (node->offset & 0x7fff);
+ return (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, uint16_t offset)
+{
+ dns_name_t tname, xname;
+ unsigned int start;
+ unsigned int n;
+ unsigned int count;
+ unsigned int hash;
+ dns_compressnode_t *node;
+ unsigned int length;
+ unsigned int tlength;
+ uint16_t toffset;
+ unsigned char *tmp;
+ isc_region_t r;
+
+ REQUIRE(VALID_CCTX(cctx));
+ REQUIRE(dns_name_isabsolute(name));
+
+ if ((cctx->allowed & DNS_COMPRESS_ENABLED) == 0)
+ return;
+
+ TABLE_READY;
+
+ if (offset >= 0x4000)
+ return;
+ dns_name_init(&tname, NULL);
+ dns_name_init(&xname, NULL);
+
+ n = dns_name_countlabels(name);
+ count = dns_name_countlabels(prefix);
+ if (dns_name_isabsolute(prefix))
+ count--;
+ if (count == 0)
+ return;
+ start = 0;
+ dns_name_toregion(name, &r);
+ length = r.length;
+ tmp = isc_mem_get(cctx->mctx, length);
+ if (tmp == NULL)
+ return;
+ /*
+ * Copy name data to 'tmp' and make 'r' use 'tmp'.
+ */
+ memmove(tmp, r.base, r.length);
+ r.base = tmp;
+ dns_name_fromregion(&xname, &r);
+
+ while (count > 0) {
+ dns_name_getlabelsequence(&xname, start, n, &tname);
+ hash = dns_name_hash(&tname, false) %
+ DNS_COMPRESS_TABLESIZE;
+ tlength = name_length(&tname);
+ toffset = (uint16_t)(offset + (length - tlength));
+ if (toffset >= 0x4000)
+ break;
+ /*
+ * 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)
+ break;
+ }
+ node->count = cctx->count++;
+ /*
+ * 'node->r.base' becomes 'tmp' when start == 0.
+ * Record this by setting 0x8000 so it can be freed later.
+ */
+ if (start == 0)
+ toffset |= 0x8000;
+ node->offset = toffset;
+ dns_name_toregion(&tname, &node->r);
+ node->labels = (uint8_t)dns_name_countlabels(&tname);
+ node->next = cctx->table[hash];
+ cctx->table[hash] = node;
+ start++;
+ n--;
+ count--;
+ }
+
+ if (start == 0)
+ isc_mem_put(cctx->mctx, tmp, length);
+}
+
+void
+dns_compress_rollback(dns_compress_t *cctx, uint16_t offset) {
+ unsigned int i;
+ dns_compressnode_t *node;
+
+ REQUIRE(VALID_CCTX(cctx));
+
+ if ((cctx->allowed & DNS_COMPRESS_ENABLED) == 0)
+ return;
+
+ if ((cctx->allowed & DNS_COMPRESS_READY) == 0)
+ return;
+
+ 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 & 0x7fff) >= offset) {
+ cctx->table[i] = node->next;
+ if ((node->offset & 0x8000) != 0)
+ isc_mem_put(cctx->mctx, node->r.base,
+ node->r.length);
+ 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..ee3e00d
--- /dev/null
+++ b/lib/dns/db.c
@@ -0,0 +1,1132 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+/***
+ *** Imports
+ ***/
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.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/clientinfo.h>
+#include <dns/db.h>
+#include <dns/dbiterator.h>
+#include <dns/log.h>
+#include <dns/master.h>
+#include <dns/rdata.h>
+#include <dns/rdataset.h>
+#include <dns/rdatasetiter.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));
+}
+
+
+bool
+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 (true);
+
+ return (false);
+}
+
+bool
+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 (true);
+
+ return (false);
+}
+
+bool
+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 (true);
+
+ return (false);
+}
+
+bool
+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));
+}
+
+bool
+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));
+}
+
+bool
+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_rdatacallbacks_t *callbacks) {
+ /*
+ * Begin loading 'db'.
+ */
+
+ REQUIRE(DNS_DB_VALID(db));
+ REQUIRE(DNS_CALLBACK_VALID(callbacks));
+
+ return ((db->methods->beginload)(db, callbacks));
+}
+
+isc_result_t
+dns_db_endload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
+ dns_dbonupdatelistener_t *listener;
+
+ /*
+ * Finish loading 'db'.
+ */
+
+ REQUIRE(DNS_DB_VALID(db));
+ REQUIRE(DNS_CALLBACK_VALID(callbacks));
+ REQUIRE(callbacks->add_private != NULL);
+
+ for (listener = ISC_LIST_HEAD(db->update_listeners);
+ listener != NULL;
+ listener = ISC_LIST_NEXT(listener, link))
+ listener->onupdate(db, listener->onupdate_arg);
+
+ return ((db->methods->endload)(db, callbacks));
+}
+
+isc_result_t
+dns_db_load(dns_db_t *db, const char *filename) {
+ return (dns_db_load3(db, filename, dns_masterformat_text, 0));
+}
+
+isc_result_t
+dns_db_load2(dns_db_t *db, const char *filename, dns_masterformat_t format) {
+ return (dns_db_load3(db, filename, format, 0));
+}
+
+isc_result_t
+dns_db_load3(dns_db_t *db, const char *filename, dns_masterformat_t format,
+ unsigned int options)
+{
+ isc_result_t result, eresult;
+ dns_rdatacallbacks_t callbacks;
+
+ /*
+ * 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);
+ 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);
+ /*
+ * 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_serialize(dns_db_t *db, dns_dbversion_t *version, FILE *file) {
+ REQUIRE(DNS_DB_VALID(db));
+ if (db->methods->serialize == NULL)
+ return (ISC_R_NOTIMPLEMENTED);
+ return ((db->methods->serialize)(db, version, file));
+}
+
+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,
+ bool commit)
+{
+ dns_dbonupdatelistener_t *listener;
+
+ /*
+ * 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);
+
+ if (commit == true) {
+ for (listener = ISC_LIST_HEAD(db->update_listeners);
+ listener != NULL;
+ listener = ISC_LIST_NEXT(listener, link))
+ listener->onupdate(db, listener->onupdate_arg);
+ }
+
+ ENSURE(*versionp == NULL);
+}
+
+/***
+ *** Node Methods
+ ***/
+
+isc_result_t
+dns_db_findnode(dns_db_t *db, dns_name_t *name,
+ bool create, dns_dbnode_t **nodep)
+{
+
+ /*
+ * Find the node with name 'name'.
+ */
+
+ REQUIRE(DNS_DB_VALID(db));
+ REQUIRE(nodep != NULL && *nodep == NULL);
+
+ if (db->methods->findnode != NULL)
+ return ((db->methods->findnode)(db, name, create, nodep));
+ else
+ return ((db->methods->findnodeext)(db, name, create,
+ NULL, NULL, nodep));
+}
+
+isc_result_t
+dns_db_findnodeext(dns_db_t *db, dns_name_t *name,
+ bool create, dns_clientinfomethods_t *methods,
+ dns_clientinfo_t *clientinfo, dns_dbnode_t **nodep)
+{
+ /*
+ * Find the node with name 'name', passing 'arg' to the database
+ * implementation.
+ */
+
+ REQUIRE(DNS_DB_VALID(db));
+ REQUIRE(nodep != NULL && *nodep == NULL);
+
+ if (db->methods->findnodeext != NULL)
+ return ((db->methods->findnodeext)(db, name, create,
+ methods, clientinfo, nodep));
+ else
+ return ((db->methods->findnode)(db, name, create, nodep));
+}
+
+isc_result_t
+dns_db_findnsec3node(dns_db_t *db, dns_name_t *name,
+ bool 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);
+ 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)));
+
+ if (db->methods->find != NULL)
+ return ((db->methods->find)(db, name, version, type,
+ options, now, nodep, foundname,
+ rdataset, sigrdataset));
+ else
+ return ((db->methods->findext)(db, name, version, type,
+ options, now, nodep, foundname,
+ NULL, NULL,
+ rdataset, sigrdataset));
+}
+
+isc_result_t
+dns_db_findext(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_clientinfomethods_t *methods, dns_clientinfo_t *clientinfo,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
+{
+
+ /*
+ * Find the best match for 'name' and 'type' in version 'version'
+ * of 'db', passing in 'arg'.
+ */
+
+ REQUIRE(DNS_DB_VALID(db));
+ REQUIRE(type != dns_rdatatype_rrsig);
+ REQUIRE(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)));
+
+ if (db->methods->findext != NULL)
+ return ((db->methods->findext)(db, name, version, type,
+ options, now, nodep, foundname,
+ methods, clientinfo,
+ rdataset, sigrdataset));
+ else
+ 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);
+ 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)
+{
+ 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, bool 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, 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), 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));
+}
+
+size_t
+dns_db_hashsize(dns_db_t *db) {
+ REQUIRE(DNS_DB_VALID(db));
+
+ if (db->methods->hashsize == NULL)
+ return (0);
+
+ return ((db->methods->hashsize)(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;
+ *dbimp = NULL;
+ 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);
+ ENSURE(*dbimp == NULL);
+}
+
+isc_result_t
+dns_db_getoriginnode(dns_db_t *db, dns_dbnode_t **nodep) {
+ REQUIRE(DNS_DB_VALID(db));
+ REQUIRE(dns_db_iszone(db) == 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_setcachestats(dns_db_t *db, isc_stats_t *stats) {
+ REQUIRE(DNS_DB_VALID(db));
+
+ if (db->methods->setcachestats != NULL)
+ return ((db->methods->setcachestats)(db, stats));
+
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+isc_result_t
+dns_db_getnsec3parameters(dns_db_t *db, dns_dbversion_t *version,
+ dns_hash_t *hash, uint8_t *flags,
+ uint16_t *iterations,
+ unsigned char *salt, size_t *salt_length)
+{
+ REQUIRE(DNS_DB_VALID(db));
+ REQUIRE(dns_db_iszone(db) == 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_getsize(dns_db_t *db, dns_dbversion_t *version, uint64_t *records,
+ uint64_t *bytes)
+{
+ REQUIRE(DNS_DB_VALID(db));
+ REQUIRE(dns_db_iszone(db) == true);
+
+ if (db->methods->getsize != NULL)
+ return ((db->methods->getsize)(db, version, records, bytes));
+
+ 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);
+}
+
+/*
+ * Attach a database to policy zone databases.
+ * This should only happen when the caller has already ensured that
+ * it is dealing with a database that understands response policy zones.
+ */
+void
+dns_db_rpz_attach(dns_db_t *db, dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num) {
+ REQUIRE(db->methods->rpz_attach != NULL);
+ (db->methods->rpz_attach)(db, rpzs, rpz_num);
+}
+
+/*
+ * Finish loading a response policy zone.
+ */
+isc_result_t
+dns_db_rpz_ready(dns_db_t *db) {
+ if (db->methods->rpz_ready == NULL)
+ return (ISC_R_SUCCESS);
+ return ((db->methods->rpz_ready)(db));
+}
+
+/**
+ * Attach a notify-on-update function the database
+ */
+isc_result_t
+dns_db_updatenotify_register(dns_db_t *db,
+ dns_dbupdate_callback_t fn,
+ void *fn_arg)
+{
+ dns_dbonupdatelistener_t *listener;
+
+ REQUIRE(db != NULL);
+ REQUIRE(fn != NULL);
+
+ listener = isc_mem_get(db->mctx, sizeof(dns_dbonupdatelistener_t));
+ if (listener == NULL)
+ return (ISC_R_NOMEMORY);
+
+ listener->onupdate = fn;
+ listener->onupdate_arg = fn_arg;
+
+ ISC_LINK_INIT(listener, link);
+ ISC_LIST_APPEND(db->update_listeners, listener, link);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_db_updatenotify_unregister(dns_db_t *db,
+ dns_dbupdate_callback_t fn,
+ void *fn_arg)
+{
+ dns_dbonupdatelistener_t *listener;
+
+ REQUIRE(db != NULL);
+
+ for (listener = ISC_LIST_HEAD(db->update_listeners);
+ listener != NULL;
+ listener = ISC_LIST_NEXT(listener, link))
+ {
+ if ((listener->onupdate == fn) &&
+ (listener->onupdate_arg == fn_arg))
+ {
+ ISC_LIST_UNLINK(db->update_listeners, listener, link);
+ isc_mem_put(db->mctx, listener,
+ sizeof(dns_dbonupdatelistener_t));
+ return (ISC_R_SUCCESS);
+ }
+ }
+
+ return (ISC_R_NOTFOUND);
+}
+
+isc_result_t
+dns_db_nodefullname(dns_db_t *db, dns_dbnode_t *node, dns_name_t *name) {
+ REQUIRE(db != NULL);
+ REQUIRE(node != NULL);
+ REQUIRE(name != NULL);
+
+ if (db->methods->nodefullname == NULL)
+ return (ISC_R_NOTIMPLEMENTED);
+ return ((db->methods->nodefullname)(db, node, name));
+}
diff --git a/lib/dns/dbiterator.c b/lib/dns/dbiterator.c
new file mode 100644
index 0000000..548f611
--- /dev/null
+++ b/lib/dns/dbiterator.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdbool.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, bool 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..3cc7cb6
--- /dev/null
+++ b/lib/dns/dbtable.c
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <config.h>
+
+#include <stdbool.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 = NULL;
+ isc_mem_attach(mctx, &dbtable->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_putanddetach(&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_putanddetach(&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;
+ bool free_dbtable = 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 = 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 *dbclone;
+
+ REQUIRE(VALID_DBTABLE(dbtable));
+ REQUIRE(dns_db_class(db) == dbtable->rdclass);
+
+ dbclone = NULL;
+ dns_db_attach(db, &dbclone);
+
+ RWLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
+ result = dns_rbt_addname(dbtable->rbt, dns_db_origin(dbclone), dbclone);
+ 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, 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..d846f04
--- /dev/null
+++ b/lib/dns/diff.c
@@ -0,0 +1,673 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include <isc/buffer.h>
+#include <isc/file.h>
+#include <isc/mem.h>
+#include <isc/print.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>
+#include <dns/time.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 = NULL;
+ isc_mem_attach(mctx, &t->mctx);
+ t->op = op;
+
+ datap = (unsigned char *)(t + 1);
+
+ memmove(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;
+
+ dns_rdata_init(&t->rdata);
+ dns_rdata_clone(rdata, &t->rdata);
+ if (rdata->data != NULL) {
+ memmove(datap, rdata->data, rdata->length);
+ t->rdata.data = datap;
+ datap += rdata->length;
+ } else {
+ t->rdata.data = NULL;
+ INSIST(rdata->length == 0);
+ }
+
+ 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;
+ isc_mem_t *mctx;
+
+ REQUIRE(DNS_DIFFTUPLE_VALID(t));
+
+ dns_name_invalidate(&t->name);
+ t->magic = 0;
+ mctx = t->mctx;
+ isc_mem_free(mctx, t);
+ isc_mem_detach(&mctx);
+ *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;
+ 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_caseequal(&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) {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_rrsig_t sig;
+ int64_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 = dns_time64_from32(sig.timeexpire);
+ 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 || dns_time64_from32(sig.timeexpire) < when)
+ when = dns_time64_from32(sig.timeexpire);
+ next_rr:
+ dns_rdata_reset(&rdata);
+ result = dns_rdataset_next(modified);
+ }
+ INSIST(result == ISC_R_NOMORE);
+ return ((isc_stdtime_t)when);
+}
+
+static void
+getownercase(dns_rdataset_t *rdataset, dns_name_t *name) {
+ if (dns_rdataset_isassociated(rdataset))
+ dns_rdataset_getownercase(rdataset, name);
+}
+
+static void
+setownercase(dns_rdataset_t *rdataset, dns_name_t *name) {
+ if (dns_rdataset_isassociated(rdataset))
+ dns_rdataset_setownercase(rdataset, name);
+}
+
+static isc_result_t
+diff_apply(dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *ver,
+ bool 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;
+ unsigned int options;
+
+ 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.
+ */
+
+ dns_rdatalist_init(&rdl);
+ rdl.type = type;
+ rdl.covers = covers;
+ rdl.rdclass = t->rdata.rdclass;
+ rdl.ttl = t->ttl;
+
+ node = NULL;
+ if (type != dns_rdatatype_nsec3 &&
+ covers != dns_rdatatype_nsec3)
+ CHECK(dns_db_findnode(db, name, true,
+ &node));
+ else
+ CHECK(dns_db_findnsec3node(db, name, true,
+ &node));
+
+ while (t != NULL &&
+ dns_name_equal(&t->name, name) &&
+ t->op == op &&
+ t->rdata.type == type &&
+ rdata_covers(&t->rdata) == covers)
+ {
+ /*
+ * Remember the add name for
+ * dns_rdataset_setownercase.
+ */
+ name = &t->name;
+ if (t->ttl != rdl.ttl && warn) {
+ 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));
+ 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);
+ }
+ ISC_LIST_APPEND(rdl.rdata, &t->rdata, link);
+ t = ISC_LIST_NEXT(t, link);
+ }
+
+ /*
+ * Convert the rdatalist into a rdataset.
+ */
+ dns_rdataset_init(&rds);
+ dns_rdataset_init(&ardataset);
+ CHECK(dns_rdatalist_tordataset(&rdl, &rds));
+ rds.trust = dns_trust_ultimate;
+
+ /*
+ * Merge the rdataset into the database.
+ */
+ switch (op) {
+ case DNS_DIFFOP_ADD:
+ case DNS_DIFFOP_ADDRESIGN:
+ options = DNS_DBADD_MERGE | DNS_DBADD_EXACT |
+ DNS_DBADD_EXACTTTL;
+ result = dns_db_addrdataset(db, node, ver,
+ 0, &rds, options,
+ &ardataset);
+ break;
+ case DNS_DIFFOP_DEL:
+ case DNS_DIFFOP_DELRESIGN:
+ options = DNS_DBSUB_EXACT | DNS_DBSUB_WANTOLD;
+ result = dns_db_subtractrdataset(db, node, ver,
+ &rds, options,
+ &ardataset);
+ break;
+ default:
+ INSIST(0);
+ }
+
+ if (result == ISC_R_SUCCESS) {
+ if (rds.type == dns_rdatatype_rrsig &&
+ (op == DNS_DIFFOP_DELRESIGN ||
+ op == DNS_DIFFOP_ADDRESIGN)) {
+ isc_stdtime_t resign;
+ resign = setresign(&ardataset);
+ dns_db_setsigningtime(db, &ardataset,
+ resign);
+ }
+ if (op == DNS_DIFFOP_ADD ||
+ op == DNS_DIFFOP_ADDRESIGN)
+ setownercase(&ardataset, name);
+ if (op == DNS_DIFFOP_DEL ||
+ op == DNS_DIFFOP_DELRESIGN)
+ getownercase(&ardataset, name);
+ } 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) {
+ dns_name_format(dns_db_origin(db),
+ namebuf,
+ sizeof(namebuf));
+ dns_rdataclass_format(dns_db_class(db),
+ classbuf,
+ sizeof(classbuf));
+ isc_log_write(DIFF_COMMON_LOGARGS,
+ ISC_LOG_WARNING,
+ "%s/%s: dns_diff_apply: "
+ "update with no effect",
+ namebuf, classbuf);
+ }
+ if (op == DNS_DIFFOP_ADD ||
+ op == DNS_DIFFOP_ADDRESIGN)
+ setownercase(&ardataset, name);
+ if (op == DNS_DIFFOP_DEL ||
+ op == DNS_DIFFOP_DELRESIGN)
+ getownercase(&ardataset, name);
+ } else if (result == DNS_R_NXRRSET) {
+ /*
+ * OK.
+ */
+ if (op == DNS_DIFFOP_DEL ||
+ op == DNS_DIFFOP_DELRESIGN)
+ getownercase(&ardataset, name);
+ if (dns_rdataset_isassociated(&ardataset))
+ dns_rdataset_disassociate(&ardataset);
+ } else {
+ if (dns_rdataset_isassociated(&ardataset))
+ dns_rdataset_disassociate(&ardataset);
+ CHECK(result);
+ }
+ dns_db_detachnode(db, &node);
+ if (dns_rdataset_isassociated(&ardataset))
+ dns_rdataset_disassociate(&ardataset);
+ }
+ }
+ 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, 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, 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);
+
+ dns_rdatalist_init(&rdl);
+ rdl.type = type;
+ rdl.covers = covers;
+ rdl.rdclass = t->rdata.rdclass;
+ rdl.ttl = t->ttl;
+
+ 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,
+ "dns_diff_load: "
+ "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);
+ 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 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);
+
+ dns_rdatalist_init(rdl);
+ rdl->type = t->rdata.type;
+ rdl->rdclass = t->rdata.rdclass;
+ rdl->ttl = t->ttl;
+ 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,
+ false, 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..d1e4c16
--- /dev/null
+++ b/lib/dns/dispatch.c
@@ -0,0 +1,3906 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.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/socket.h>
+#include <isc/stats.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;
+
+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;
+ isc_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 rng_lock. */
+ isc_mutex_t rng_lock;
+ isc_rng_t *rngctx; /*%< RNG 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 depool_lock;
+ isc_mempool_t *depool; /*%< pool for dispatch events */
+ isc_mutex_t rpool_lock;
+ isc_mempool_t *rpool; /*%< pool for replies */
+ isc_mutex_t dpool_lock;
+ isc_mempool_t *dpool; /*%< dispatch allocations */
+ isc_mutex_t bpool_lock;
+ isc_mempool_t *bpool; /*%< pool for buffers */
+ isc_mutex_t spool_lock;
+ isc_mempool_t *spool; /*%< pool for dispsocks */
+
+ /*%
+ * 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;
+ bool 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 */
+ isc_sockaddr_t peer; /*%< peer address (TCP) */
+ isc_dscp_t dscp; /*%< "listen-on" DSCP value */
+ unsigned int maxrequests; /*%< max requests */
+ isc_event_t *ctlevent;
+
+ isc_mutex_t sepool_lock;
+ isc_mempool_t *sepool; /*%< pool for socket events */
+
+ /*% 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;
+ isc_rng_t *rngctx; /*%< 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_RNGCTX(disp) ((disp)->socktype == isc_sockettype_udp) ? \
+ ((disp)->rngctx) : ((disp)->mgr->rngctx)
+
+/*%
+ * 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 bool 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 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_devent(dns_dispatch_t *disp, dns_dispatchevent_t *ev);
+static inline dns_dispatchevent_t *allocate_devent(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,
+ isc_socket_t *dup_socket);
+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_socket_t *dup_socket);
+static bool 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,
+ bool 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,
+ isc_socket_t *dup_socket);
+static bool 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 inline void
+inc_stats(dns_dispatchmgr_t *mgr, isc_statscounter_t counter) {
+ if (mgr->stats != NULL)
+ isc_stats_increment(mgr->stats, counter);
+}
+
+static inline void
+dec_stats(dns_dispatchmgr_t *mgr, isc_statscounter_t counter) {
+ if (mgr->stats != NULL)
+ isc_stats_decrement(mgr->stats, counter);
+}
+
+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);
+ }
+}
+
+/*
+ * Return a hash of the destination and message id.
+ */
+static uint32_t
+dns_hash(dns_qid_t *qid, isc_sockaddr_t *dest, dns_messageid_t id,
+ in_port_t port)
+{
+ uint32_t ret;
+
+ ret = isc_sockaddr_hash(dest, true);
+ ret ^= ((uint32_t)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 bool
+destroy_disp_ok(dns_dispatch_t *disp)
+{
+ if (disp->refcount != 0)
+ return (false);
+
+ if (disp->recv_pending != 0)
+ return (false);
+
+ if (!ISC_LIST_EMPTY(disp->activesockets))
+ return (false);
+
+ if (disp->shutting_down == 0)
+ return (false);
+
+ return (true);
+}
+
+/*
+ * Called when refcount reaches 0 (and safe to destroy).
+ *
+ * The dispatcher must be locked.
+ * The manager must not be locked.
+ */
+static void
+destroy_disp(isc_task_t *task, isc_event_t *event) {
+ dns_dispatch_t *disp;
+ dns_dispatchmgr_t *mgr;
+ bool 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->sepool != NULL) {
+ isc_mempool_destroy(&disp->sepool);
+ (void)isc_mutex_destroy(&disp->sepool_lock);
+ }
+
+ 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;
+ dns_qid_t *qid;
+
+ REQUIRE(disp->port_table != NULL);
+
+ portentry = isc_mempool_get(disp->portpool);
+ if (portentry == NULL)
+ return (portentry);
+
+ portentry->port = port;
+ portentry->refs = 1;
+ ISC_LINK_INIT(portentry, link);
+ qid = DNS_QID(disp);
+ LOCK(&qid->lock);
+ ISC_LIST_APPEND(disp->port_table[port % DNS_DISPATCH_PORTTABLESIZE],
+ portentry, link);
+ UNLOCK(&qid->lock);
+
+ return (portentry);
+}
+
+/*%
+ * The caller must not hold the qid->lock.
+ */
+static void
+deref_portentry(dns_dispatch_t *disp, dispportentry_t **portentryp) {
+ dispportentry_t *portentry = *portentryp;
+ dns_qid_t *qid;
+
+ REQUIRE(disp->port_table != NULL);
+ REQUIRE(portentry != NULL && portentry->refs > 0);
+
+ qid = DNS_QID(disp);
+ LOCK(&qid->lock);
+ 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);
+ }
+
+ /*
+ * Set '*portentryp' to NULL inside the lock so that
+ * dispsock->portentry does not change in socket_search.
+ */
+ *portentryp = NULL;
+
+ UNLOCK(&qid->lock);
+}
+
+/*%
+ * Find a dispsocket for socket address 'dest', and port number 'port'.
+ * Return NULL if no such entry exists. Requires qid->lock to be held.
+ */
+static dispsocket_t *
+socket_search(dns_qid_t *qid, isc_sockaddr_t *dest, in_port_t port,
+ unsigned int bucket)
+{
+ dispsocket_t *dispsock;
+
+ REQUIRE(VALID_QID(qid));
+ REQUIRE(bucket < qid->qid_nbuckets);
+
+ dispsock = ISC_LIST_HEAD(qid->sock_table[bucket]);
+
+ while (dispsock != NULL) {
+ if (dispsock->portentry != NULL &&
+ dispsock->portentry->port == port &&
+ isc_sockaddr_equal(dest, &dispsock->host))
+ 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
+ */
+static isc_result_t
+get_dispsocket(dns_dispatch_t *disp, isc_sockaddr_t *dest,
+ isc_socketmgr_t *sockmgr, dispsocket_t **dispsockp,
+ in_port_t *portp)
+{
+ int i;
+ 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;
+ dispportentry_t *portentry = NULL;
+ dns_qid_t *qid;
+
+ 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;
+ qid = DNS_QID(disp);
+
+ for (i = 0; i < 64; i++) {
+ port = ports[isc_rng_uniformrandom(DISP_RNGCTX(disp), nports)];
+ isc_sockaddr_setport(&localaddr, port);
+
+ LOCK(&qid->lock);
+ bucket = dns_hash(qid, dest, 0, port);
+ if (socket_search(qid, dest, port, bucket) != NULL) {
+ UNLOCK(&qid->lock);
+ continue;
+ }
+ UNLOCK(&qid->lock);
+ bindoptions = 0;
+ portentry = port_search(disp, port);
+
+ if (portentry != NULL)
+ bindoptions |= ISC_SOCKET_REUSEADDRESS;
+ result = open_socket(sockmgr, &localaddr, bindoptions, &sock,
+ NULL);
+ if (result == ISC_R_SUCCESS) {
+ if (portentry == NULL) {
+ portentry = new_portentry(disp, port);
+ if (portentry == NULL) {
+ result = ISC_R_NOMEMORY;
+ break;
+ }
+ } else {
+ LOCK(&qid->lock);
+ portentry->refs++;
+ UNLOCK(&qid->lock);
+ }
+ break;
+ } else if (result == ISC_R_NOPERM) {
+ char buf[ISC_SOCKADDR_FORMATSIZE];
+ isc_sockaddr_format(&localaddr, buf, sizeof(buf));
+ dispatch_log(disp, ISC_LOG_WARNING,
+ "open_socket(%s) -> %s: continuing",
+ buf, isc_result_totext(result));
+ } else if (result != ISC_R_ADDRINUSE)
+ break;
+ }
+
+ if (result == ISC_R_SUCCESS) {
+ dispsock->socket = sock;
+ dispsock->host = *dest;
+ dispsock->portentry = portentry;
+ dispsock->bucket = bucket;
+ LOCK(&qid->lock);
+ ISC_LIST_APPEND(qid->sock_table[bucket], dispsock, blink);
+ UNLOCK(&qid->lock);
+ *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(VALID_QID(qid));
+ 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) {
+ isc_mempool_t *bpool;
+ 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--;
+ bpool = disp->mgr->bpool;
+ UNLOCK(&disp->mgr->buffer_lock);
+ isc_mempool_put(bpool, buf);
+ break;
+ default:
+ INSIST(0);
+ break;
+ }
+}
+
+static void *
+allocate_udp_buffer(dns_dispatch_t *disp) {
+ isc_mempool_t *bpool;
+ void *temp;
+
+ LOCK(&disp->mgr->buffer_lock);
+ bpool = disp->mgr->bpool;
+ disp->mgr->buffers++;
+ UNLOCK(&disp->mgr->buffer_lock);
+
+ temp = isc_mempool_get(bpool);
+
+ if (temp == NULL) {
+ LOCK(&disp->mgr->buffer_lock);
+ disp->mgr->buffers--;
+ UNLOCK(&disp->mgr->buffer_lock);
+ }
+
+ return (temp);
+}
+
+static inline void
+free_sevent(isc_event_t *ev) {
+ isc_mempool_t *pool = ev->ev_destroy_arg;
+ isc_socketevent_t *sev = (isc_socketevent_t *) ev;
+ isc_mempool_put(pool, sev);
+}
+
+static inline isc_socketevent_t *
+allocate_sevent(dns_dispatch_t *disp, isc_socket_t *sock,
+ isc_eventtype_t type, isc_taskaction_t action, const void *arg)
+{
+ isc_socketevent_t *ev;
+ void *deconst_arg;
+
+ ev = isc_mempool_get(disp->sepool);
+ if (ev == NULL)
+ return (NULL);
+ DE_CONST(arg, deconst_arg);
+ ISC_EVENT_INIT(ev, sizeof(*ev), 0, NULL, type,
+ action, deconst_arg, sock,
+ free_sevent, disp->sepool);
+ ev->result = ISC_R_UNSET;
+ ISC_LINK_INIT(ev, ev_link);
+ ISC_LIST_INIT(ev->bufferlist);
+ ev->region.base = NULL;
+ ev->n = 0;
+ ev->offset = 0;
+ ev->attributes = 0;
+
+ return (ev);
+}
+
+
+static inline void
+free_devent(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->depool, ev);
+}
+
+static inline dns_dispatchevent_t *
+allocate_devent(dns_dispatch_t *disp) {
+ dns_dispatchevent_t *ev;
+
+ ev = isc_mempool_get(disp->mgr->depool);
+ 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;
+ bool killit;
+ bool queue_response;
+ dns_dispatchmgr_t *mgr;
+ dns_qid_t *qid;
+ isc_netaddr_t netaddr;
+ int match;
+ int result;
+ bool qidlocked = 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);
+
+ isc_event_free(&ev_in);
+ UNLOCK(&disp->lock);
+ 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));
+
+ isc_event_free(&ev_in);
+ UNLOCK(&disp->lock);
+ 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.
+ */
+ 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 = 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) {
+ inc_stats(mgr, 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");
+ inc_stats(mgr, 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 each dispatch is bound to a different address
+ * then fail.
+ *
+ * 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(&disp->local, &resp->disp->local) &&
+ !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_devent(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 = 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);
+ }
+ isc_event_free(&ev_in);
+ UNLOCK(&disp->lock);
+}
+
+/*
+ * 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;
+ bool killit;
+ bool 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.
+ */
+ 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_devent(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 = 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);
+
+ isc_event_free(&ev_in);
+ UNLOCK(&disp->lock);
+}
+
+/*
+ * 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 *sock;
+
+ 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)
+ sock = dispsock->socket;
+ else
+ sock = disp->socket;
+ INSIST(sock != 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) {
+ isc_task_t *dt = dispsock->task;
+ isc_socketevent_t *sev =
+ allocate_sevent(disp, sock,
+ ISC_SOCKEVENT_RECVDONE,
+ udp_exrecv, dispsock);
+ if (sev == NULL) {
+ free_buffer(disp, region.base, region.length);
+ return (ISC_R_NOMEMORY);
+ }
+
+ res = isc_socket_recv2(sock, &region, 1, dt, sev, 0);
+ if (res != ISC_R_SUCCESS) {
+ free_buffer(disp, region.base, region.length);
+ return (res);
+ }
+ } else {
+ isc_task_t *dt = disp->task[0];
+ isc_socketevent_t *sev =
+ allocate_sevent(disp, sock,
+ ISC_SOCKEVENT_RECVDONE,
+ udp_shrecv, disp);
+ if (sev == NULL) {
+ free_buffer(disp, region.base, region.length);
+ return (ISC_R_NOMEMORY);
+ }
+
+ res = isc_socket_recv2(sock, &region, 1, dt, sev, 0);
+ 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 bool
+destroy_mgr_ok(dns_dispatchmgr_t *mgr) {
+ mgr_log(mgr, LVL(90),
+ "destroy_mgr_ok: shuttingdown=%d, listnonempty=%d, "
+ "depool=%d, rpool=%d, dpool=%d",
+ MGR_IS_SHUTTINGDOWN(mgr), !ISC_LIST_EMPTY(mgr->list),
+ isc_mempool_getallocated(mgr->depool),
+ isc_mempool_getallocated(mgr->rpool),
+ isc_mempool_getallocated(mgr->dpool));
+ if (!MGR_IS_SHUTTINGDOWN(mgr))
+ return (false);
+ if (!ISC_LIST_EMPTY(mgr->list))
+ return (false);
+ if (isc_mempool_getallocated(mgr->depool) != 0)
+ return (false);
+ if (isc_mempool_getallocated(mgr->rpool) != 0)
+ return (false);
+ if (isc_mempool_getallocated(mgr->dpool) != 0)
+ return (false);
+
+ return (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;
+
+ if (mgr->rngctx != NULL)
+ isc_rng_detach(&mgr->rngctx);
+ DESTROYLOCK(&mgr->rng_lock);
+
+ isc_mempool_destroy(&mgr->depool);
+ isc_mempool_destroy(&mgr->rpool);
+ isc_mempool_destroy(&mgr->dpool);
+ if (mgr->bpool != NULL)
+ isc_mempool_destroy(&mgr->bpool);
+ if (mgr->spool != NULL)
+ isc_mempool_destroy(&mgr->spool);
+
+ DESTROYLOCK(&mgr->spool_lock);
+ DESTROYLOCK(&mgr->bpool_lock);
+ DESTROYLOCK(&mgr->dpool_lock);
+ DESTROYLOCK(&mgr->rpool_lock);
+ DESTROYLOCK(&mgr->depool_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)
+ isc_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 *dup_socket)
+{
+ isc_socket_t *sock;
+ isc_result_t result;
+
+ sock = *sockp;
+ if (sock != NULL) {
+ result = isc_socket_open(sock);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ } else if (dup_socket != NULL) {
+ result = isc_socket_dup(dup_socket, &sock);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ isc_socket_setname(sock, "dispatcher", NULL);
+ *sockp = sock;
+ return (ISC_R_SUCCESS);
+ } else {
+ 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);
+
+#ifndef ISC_ALLOW_MAPPED
+ isc_socket_ipv6only(sock, 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;
+ mgr->rngctx = NULL;
+
+ result = isc_mutex_init(&mgr->lock);
+ if (result != ISC_R_SUCCESS)
+ goto deallocate;
+
+ result = isc_mutex_init(&mgr->rng_lock);
+ if (result != ISC_R_SUCCESS)
+ goto kill_lock;
+
+ result = isc_mutex_init(&mgr->buffer_lock);
+ if (result != ISC_R_SUCCESS)
+ goto kill_rng_lock;
+
+ result = isc_mutex_init(&mgr->depool_lock);
+ if (result != ISC_R_SUCCESS)
+ goto kill_buffer_lock;
+
+ result = isc_mutex_init(&mgr->rpool_lock);
+ if (result != ISC_R_SUCCESS)
+ goto kill_depool_lock;
+
+ result = isc_mutex_init(&mgr->dpool_lock);
+ if (result != ISC_R_SUCCESS)
+ goto kill_rpool_lock;
+
+ result = isc_mutex_init(&mgr->bpool_lock);
+ if (result != ISC_R_SUCCESS)
+ goto kill_dpool_lock;
+
+ result = isc_mutex_init(&mgr->spool_lock);
+ if (result != ISC_R_SUCCESS)
+ goto kill_bpool_lock;
+
+ mgr->depool = NULL;
+ if (isc_mempool_create(mgr->mctx, sizeof(dns_dispatchevent_t),
+ &mgr->depool) != ISC_R_SUCCESS) {
+ result = ISC_R_NOMEMORY;
+ goto kill_spool_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_depool;
+ }
+
+ 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->depool, "dispmgr_depool");
+ isc_mempool_setmaxalloc(mgr->depool, 32768);
+ isc_mempool_setfreemax(mgr->depool, 32768);
+ isc_mempool_associatelock(mgr->depool, &mgr->depool_lock);
+ isc_mempool_setfillcount(mgr->depool, 32);
+
+ isc_mempool_setname(mgr->rpool, "dispmgr_rpool");
+ isc_mempool_setmaxalloc(mgr->rpool, 32768);
+ isc_mempool_setfreemax(mgr->rpool, 32768);
+ isc_mempool_associatelock(mgr->rpool, &mgr->rpool_lock);
+ isc_mempool_setfillcount(mgr->rpool, 32);
+
+ isc_mempool_setname(mgr->dpool, "dispmgr_dpool");
+ isc_mempool_setmaxalloc(mgr->dpool, 32768);
+ isc_mempool_setfreemax(mgr->dpool, 32768);
+ isc_mempool_associatelock(mgr->dpool, &mgr->dpool_lock);
+ isc_mempool_setfillcount(mgr->dpool, 32);
+
+ 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);
+
+ result = isc_rng_create(mctx, mgr->entropy, &mgr->rngctx);
+ if (result != ISC_R_SUCCESS)
+ goto kill_dpool;
+
+ *mgrp = mgr;
+ return (ISC_R_SUCCESS);
+
+ kill_dpool:
+ isc_mempool_destroy(&mgr->dpool);
+ kill_rpool:
+ isc_mempool_destroy(&mgr->rpool);
+ kill_depool:
+ isc_mempool_destroy(&mgr->depool);
+ kill_spool_lock:
+ DESTROYLOCK(&mgr->spool_lock);
+ kill_bpool_lock:
+ DESTROYLOCK(&mgr->bpool_lock);
+ kill_dpool_lock:
+ DESTROYLOCK(&mgr->dpool_lock);
+ kill_rpool_lock:
+ DESTROYLOCK(&mgr->rpool_lock);
+ kill_depool_lock:
+ DESTROYLOCK(&mgr->depool_lock);
+ kill_buffer_lock:
+ DESTROYLOCK(&mgr->buffer_lock);
+ kill_rng_lock:
+ DESTROYLOCK(&mgr->rng_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) {
+ /*
+ * We only increase the maxbuffers to avoid accidental buffer
+ * shortage. Ideally we'd separate the manager-wide maximum
+ * from per-dispatch limits and respect the latter within the
+ * global limit. But at this moment that's deemed to be
+ * overkilling and isn't worth additional implementation
+ * complexity.
+ */
+ if (maxbuffers > mgr->maxbuffers) {
+ isc_mempool_setmaxalloc(mgr->bpool, maxbuffers);
+ isc_mempool_setfreemax(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_setfreemax(mgr->bpool, maxbuffers);
+ isc_mempool_associatelock(mgr->bpool, &mgr->bpool_lock);
+ isc_mempool_setfillcount(mgr->bpool, 32);
+ }
+
+ /* Create or adjust socket pool */
+ if (mgr->spool != NULL) {
+ if (maxrequests < DNS_DISPATCH_POOLSOCKS * 2) {
+ isc_mempool_setmaxalloc(mgr->spool,
+ DNS_DISPATCH_POOLSOCKS * 2);
+ isc_mempool_setfreemax(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)
+ goto cleanup;
+
+ isc_mempool_setname(mgr->spool, "dispmgr_spool");
+ isc_mempool_setmaxalloc(mgr->spool, maxrequests);
+ isc_mempool_setfreemax(mgr->spool, maxrequests);
+ isc_mempool_associatelock(mgr->spool, &mgr->spool_lock);
+ isc_mempool_setfillcount(mgr->spool, 32);
+
+ result = qid_allocate(mgr, buckets, increment, &mgr->qid, 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;
+ bool 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, isc_stats_t *stats) {
+ REQUIRE(VALID_DISPATCHMGR(mgr));
+ REQUIRE(ISC_LIST_EMPTY(mgr->list));
+ REQUIRE(mgr->stats == NULL);
+
+ isc_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 bool
+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;
+ bool available = 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 = true;
+
+unlock:
+ PORTBUFUNLOCK(mgr);
+ return (available);
+}
+
+#define ATTRMATCH(_a1, _a2, _mask) (((_a1) & (_mask)) == ((_a2) & (_mask)))
+
+static bool
+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 (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 (false);
+ }
+
+ /*
+ * Check if we match the binding <address,port>.
+ * Wildcard ports match/fail here.
+ */
+ if (isc_sockaddr_equal(&disp->local, addr))
+ return (true);
+ if (isc_sockaddr_getport(addr) == 0)
+ return (false);
+
+ /*
+ * Check if we match a bound wildcard port <address,port>.
+ */
+ if (!isc_sockaddr_eqaddr(&disp->local, addr))
+ return (false);
+ result = isc_socket_getsockname(disp->socket, &sockaddr);
+ if (result != ISC_R_SUCCESS)
+ return (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,
+ bool 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->qid_table,
+ buckets * sizeof(dns_displist_t));
+ isc_mem_put(mgr->mctx, qid, sizeof(*qid));
+ 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));
+ memset(&disp->peer, 0, sizeof(disp->peer));
+ 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;
+ disp->rngctx = NULL;
+ isc_rng_attach(mgr->rngctx, &disp->rngctx);
+ disp->port_table = NULL;
+ disp->portpool = NULL;
+ disp->dscp = -1;
+
+ result = isc_mutex_init(&disp->lock);
+ if (result != ISC_R_SUCCESS)
+ goto deallocate;
+
+ disp->failsafe_ev = allocate_devent(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:
+ if (disp->rngctx != NULL)
+ isc_rng_detach(&disp->rngctx);
+ isc_mempool_put(mgr->dpool, disp);
+
+ return (result);
+}
+
+
+/*
+ * MUST be unlocked, and not used by anything.
+ */
+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->depool, 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);
+
+ if (disp->rngctx != NULL)
+ isc_rng_detach(&disp->rngctx);
+
+ 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)
+{
+
+ attributes |= DNS_DISPATCHATTR_PRIVATE; /* XXXMLG */
+
+ return (dns_dispatch_createtcp2(mgr, sock, taskmgr, NULL, NULL,
+ buffersize, maxbuffers, maxrequests,
+ buckets, increment, attributes,
+ dispp));
+}
+
+isc_result_t
+dns_dispatch_createtcp2(dns_dispatchmgr_t *mgr, isc_socket_t *sock,
+ isc_taskmgr_t *taskmgr, isc_sockaddr_t *localaddr,
+ isc_sockaddr_t *destaddr, 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);
+
+ if (destaddr == NULL)
+ 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, false);
+ if (result != ISC_R_SUCCESS)
+ goto deallocate_dispatch;
+
+ disp->socktype = isc_sockettype_tcp;
+ disp->socket = NULL;
+ isc_socket_attach(sock, &disp->socket);
+
+ disp->sepool = NULL;
+
+ 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;
+
+ if (localaddr == NULL) {
+ if (destaddr != NULL) {
+ switch (isc_sockaddr_pf(destaddr)) {
+ case AF_INET:
+ isc_sockaddr_any(&disp->local);
+ break;
+ case AF_INET6:
+ isc_sockaddr_any6(&disp->local);
+ break;
+ }
+ }
+ } else
+ disp->local = *localaddr;
+
+ if (destaddr != NULL)
+ disp->peer = *destaddr;
+
+ /*
+ * 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_gettcp(dns_dispatchmgr_t *mgr, isc_sockaddr_t *destaddr,
+ isc_sockaddr_t *localaddr, dns_dispatch_t **dispp)
+{
+ dns_dispatch_t *disp;
+ isc_result_t result;
+ isc_sockaddr_t peeraddr;
+ isc_sockaddr_t sockname;
+ unsigned int attributes, mask;
+ bool match = false;
+
+ REQUIRE(VALID_DISPATCHMGR(mgr));
+ REQUIRE(destaddr != NULL);
+ REQUIRE(dispp != NULL && *dispp == NULL);
+
+ attributes = DNS_DISPATCHATTR_TCP | DNS_DISPATCHATTR_CONNECTED;
+ mask = DNS_DISPATCHATTR_TCP | DNS_DISPATCHATTR_PRIVATE |
+ DNS_DISPATCHATTR_EXCLUSIVE | DNS_DISPATCHATTR_CONNECTED;
+
+ LOCK(&mgr->lock);
+ disp = ISC_LIST_HEAD(mgr->list);
+ while (disp != NULL && !match) {
+ LOCK(&disp->lock);
+ if ((disp->shutting_down == 0) &&
+ ATTRMATCH(disp->attributes, attributes, mask) &&
+ (localaddr == NULL ||
+ isc_sockaddr_eqaddr(localaddr, &disp->local))) {
+ result = isc_socket_getsockname(disp->socket,
+ &sockname);
+ if (result == ISC_R_SUCCESS)
+ result = isc_socket_getpeername(disp->socket,
+ &peeraddr);
+ if (result == ISC_R_SUCCESS &&
+ isc_sockaddr_equal(destaddr, &peeraddr) &&
+ (localaddr == NULL ||
+ isc_sockaddr_eqaddr(localaddr, &sockname))) {
+ /* attach */
+ disp->refcount++;
+ *dispp = disp;
+ match = true;
+ }
+ }
+ UNLOCK(&disp->lock);
+ disp = ISC_LIST_NEXT(disp, link);
+ }
+ UNLOCK(&mgr->lock);
+ return (match ? ISC_R_SUCCESS : ISC_R_NOTFOUND);
+}
+
+isc_result_t
+dns_dispatch_gettcp2(dns_dispatchmgr_t *mgr, isc_sockaddr_t *destaddr,
+ isc_sockaddr_t *localaddr, bool *connected,
+ dns_dispatch_t **dispp)
+{
+ dns_dispatch_t *disp;
+ isc_result_t result;
+ isc_sockaddr_t peeraddr;
+ isc_sockaddr_t sockname;
+ unsigned int attributes, mask;
+ bool match = false;
+
+ REQUIRE(VALID_DISPATCHMGR(mgr));
+ REQUIRE(destaddr != NULL);
+ REQUIRE(dispp != NULL && *dispp == NULL);
+ REQUIRE(connected != NULL);
+
+ /* First pass (same as dns_dispatch_gettcp()) */
+ attributes = DNS_DISPATCHATTR_TCP | DNS_DISPATCHATTR_CONNECTED;
+ mask = DNS_DISPATCHATTR_TCP | DNS_DISPATCHATTR_PRIVATE |
+ DNS_DISPATCHATTR_EXCLUSIVE | DNS_DISPATCHATTR_CONNECTED;
+
+ LOCK(&mgr->lock);
+ disp = ISC_LIST_HEAD(mgr->list);
+ while (disp != NULL && !match) {
+ LOCK(&disp->lock);
+ if ((disp->shutting_down == 0) &&
+ ATTRMATCH(disp->attributes, attributes, mask) &&
+ (localaddr == NULL ||
+ isc_sockaddr_eqaddr(localaddr, &disp->local))) {
+ result = isc_socket_getsockname(disp->socket,
+ &sockname);
+ if (result == ISC_R_SUCCESS)
+ result = isc_socket_getpeername(disp->socket,
+ &peeraddr);
+ if (result == ISC_R_SUCCESS &&
+ isc_sockaddr_equal(destaddr, &peeraddr) &&
+ (localaddr == NULL ||
+ isc_sockaddr_eqaddr(localaddr, &sockname))) {
+ /* attach */
+ disp->refcount++;
+ *dispp = disp;
+ match = true;
+ *connected = true;
+ }
+ }
+ UNLOCK(&disp->lock);
+ disp = ISC_LIST_NEXT(disp, link);
+ }
+ if (match) {
+ UNLOCK(&mgr->lock);
+ return (ISC_R_SUCCESS);
+ }
+
+ /* Second pass */
+ attributes = DNS_DISPATCHATTR_TCP;
+
+ disp = ISC_LIST_HEAD(mgr->list);
+ while (disp != NULL && !match) {
+ LOCK(&disp->lock);
+ if ((disp->shutting_down == 0) &&
+ ATTRMATCH(disp->attributes, attributes, mask) &&
+ (localaddr == NULL ||
+ isc_sockaddr_eqaddr(localaddr, &disp->local)) &&
+ isc_sockaddr_equal(destaddr, &disp->peer)) {
+ /* attach */
+ disp->refcount++;
+ *dispp = disp;
+ match = true;
+ }
+ UNLOCK(&disp->lock);
+ disp = ISC_LIST_NEXT(disp, link);
+ }
+ UNLOCK(&mgr->lock);
+ return (match ? ISC_R_SUCCESS : ISC_R_NOTFOUND);
+}
+
+isc_result_t
+dns_dispatch_getudp_dup(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, dns_dispatch_t *dup_dispatch)
+{
+ 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.
+ */
+ if (dup_dispatch == NULL) {
+ 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,
+ dup_dispatch == NULL
+ ? NULL
+ : dup_dispatch->socket);
+
+ if (result != ISC_R_SUCCESS) {
+ UNLOCK(&mgr->lock);
+ return (result);
+ }
+
+ UNLOCK(&mgr->lock);
+ *dispp = disp;
+
+ return (ISC_R_SUCCESS);
+}
+
+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)
+{
+ return (dns_dispatch_getudp_dup(mgr, sockmgr, taskmgr, localaddr,
+ buffersize, maxbuffers, maxrequests,
+ buckets, increment, attributes,
+ mask, dispp, NULL));
+}
+
+/*
+ * 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, isc_socket_t *dup_socket)
+{
+ 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;
+ bool anyport;
+
+ INSIST(sockp != NULL && *sockp == NULL);
+
+ localaddr_bound = *localaddr;
+ anyport = (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(localaddr) == 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[isc_rng_uniformrandom(DISP_RNGCTX(disp),
+ nports)];
+ isc_sockaddr_setport(&localaddr_bound, prt);
+ result = open_socket(sockmgr, &localaddr_bound,
+ 0, &sock, NULL);
+ /*
+ * Continue if the port choosen is already in use
+ * or the OS has reserved it.
+ */
+ if (result == ISC_R_NOPERM ||
+ result == ISC_R_ADDRINUSE)
+ continue;
+ 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,
+ dup_socket);
+
+ 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, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto end;
+ 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_socket_t *dup_socket)
+{
+ 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);
+
+ disp->socktype = isc_sockettype_udp;
+
+ if ((attributes & DNS_DISPATCHATTR_EXCLUSIVE) == 0) {
+ result = get_udpsocket(mgr, disp, sockmgr, localaddr, &sock,
+ dup_socket);
+ if (result != ISC_R_SUCCESS)
+ goto deallocate_dispatch;
+
+ if (isc_log_wouldlog(dns_lctx, 90)) {
+ char addrbuf[ISC_SOCKADDR_FORMATSIZE];
+
+ isc_sockaddr_format(localaddr, addrbuf,
+ ISC_SOCKADDR_FORMATSIZE);
+ mgr_log(mgr, LVL(90), "dns_dispatch_createudp: Created"
+ " UDP dispatch for %s with socket fd %d",
+ addrbuf, isc_socket_getfd(sock));
+ }
+
+ } 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, NULL);
+ 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->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_shutdown(disp->task[i]);
+ isc_task_detach(&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;
+ }
+
+ disp->sepool = NULL;
+ if (isc_mempool_create(mgr->mctx, sizeof(isc_socketevent_t),
+ &disp->sepool) != ISC_R_SUCCESS)
+ {
+ result = ISC_R_NOMEMORY;
+ goto kill_ctlevent;
+ }
+
+ result = isc_mutex_init(&disp->sepool_lock);
+ if (result != ISC_R_SUCCESS)
+ goto kill_sepool;
+
+ isc_mempool_setname(disp->sepool, "disp_sepool");
+ isc_mempool_setmaxalloc(disp->sepool, 32768);
+ isc_mempool_setfreemax(disp->sepool, 32768);
+ isc_mempool_associatelock(disp->sepool, &disp->sepool_lock);
+ isc_mempool_setfillcount(disp->sepool, 16);
+
+ 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_sepool:
+ isc_mempool_destroy(&disp->sepool);
+ kill_ctlevent:
+ isc_event_free(&disp->ctlevent);
+ 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;
+ bool killit;
+
+ REQUIRE(dispp != NULL && VALID_DISPATCH(*dispp));
+
+ disp = *dispp;
+ *dispp = NULL;
+
+ LOCK(&disp->lock);
+
+ INSIST(disp->refcount > 0);
+ disp->refcount--;
+ 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)
+{
+ return (dns_dispatch_addresponse3(disp, 0, dest, task, action, arg,
+ idp, resp, sockmgr));
+}
+
+isc_result_t
+dns_dispatch_addresponse3(dns_dispatch_t *disp, unsigned int options,
+ 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;
+ bool 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_devent(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 = true;
+ isc_task_send(oldestresp->task,
+ ISC_EVENT_PTR(&rev));
+ inc_stats(disp->mgr,
+ dns_resstatscounter_dispabort);
+ }
+ }
+
+ /*
+ * 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);
+
+ if ((disp->attributes & DNS_DISPATCHATTR_EXCLUSIVE) != 0) {
+ /*
+ * Get a separate UDP socket with a random port number.
+ */
+ result = get_dispsocket(disp, dest, sockmgr, &dispsocket,
+ &localport);
+ if (result != ISC_R_SUCCESS) {
+ UNLOCK(&disp->lock);
+ inc_stats(disp->mgr, dns_resstatscounter_dispsockfail);
+ return (result);
+ }
+ } else {
+ localport = disp->localport;
+ }
+
+ /*
+ * Try somewhat hard to find an unique ID unless FIXEDID is set
+ * in which case we use the id passed in via *idp.
+ */
+ LOCK(&qid->lock);
+ if ((options & DNS_DISPATCHOPT_FIXEDID) != 0)
+ id = *idp;
+ else
+ id = (dns_messageid_t)isc_rng_random(DISP_RNGCTX(disp));
+ ok = false;
+ i = 0;
+ do {
+ bucket = dns_hash(qid, dest, id, localport);
+ if (entry_search(qid, dest, id, localport, bucket) == NULL) {
+ ok = true;
+ break;
+ }
+ if ((disp->attributes & DNS_DISPATCHATTR_FIXEDID) != 0)
+ break;
+ id += qid->qid_increment;
+ id &= 0x0000ffff;
+ } while (i++ < 64);
+ UNLOCK(&qid->lock);
+
+ if (!ok) {
+ UNLOCK(&disp->lock);
+ return (ISC_R_NOMORE);
+ }
+
+ res = isc_mempool_get(disp->mgr->rpool);
+ if (res == NULL) {
+ if (dispsocket != NULL)
+ destroy_dispsocket(disp, &dispsocket);
+ UNLOCK(&disp->lock);
+ 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 = false;
+ ISC_LIST_INIT(res->items);
+ ISC_LINK_INIT(res, link);
+ res->magic = RESPONSE_MAGIC;
+
+ LOCK(&qid->lock);
+ ISC_LIST_APPEND(qid->qid_table[bucket], res, link);
+ UNLOCK(&qid->lock);
+
+ inc_stats(disp->mgr, (qid == disp->mgr->qid) ?
+ dns_resstatscounter_disprequdp :
+ dns_resstatscounter_dispreqtcp);
+
+ 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--;
+
+ dec_stats(disp->mgr, (qid == disp->mgr->qid) ?
+ dns_resstatscounter_disprequdp :
+ dns_resstatscounter_dispreqtcp);
+
+ 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_addresponse3(disp, 0, 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);
+ if ((disp->attributes & DNS_DISPATCHATTR_CONNECTED) == 0) {
+ disp->attributes |= DNS_DISPATCHATTR_CONNECTED;
+ (void)startrecv(disp, NULL);
+ }
+ UNLOCK(&disp->lock);
+}
+
+isc_result_t
+dns_dispatch_getnext(dns_dispentry_t *resp, dns_dispatchevent_t **sockevent) {
+ dns_dispatch_t *disp;
+ dns_dispatchevent_t *ev;
+
+ REQUIRE(VALID_RESPONSE(resp));
+ REQUIRE(sockevent != NULL && *sockevent != NULL);
+
+ disp = resp->disp;
+ REQUIRE(VALID_DISPATCH(disp));
+
+ REQUIRE(resp->item_out == true);
+ resp->item_out = false;
+
+ ev = *sockevent;
+ *sockevent = NULL;
+
+ LOCK(&disp->lock);
+ if (ev->buffer.base != NULL)
+ free_buffer(disp, ev->buffer.base, ev->buffer.length);
+ free_devent(disp, ev);
+
+ if (disp->shutting_down == 1) {
+ UNLOCK(&disp->lock);
+ return (ISC_R_SHUTTINGDOWN);
+ }
+ ev = ISC_LIST_HEAD(resp->items);
+ if (ev != NULL) {
+ ISC_LIST_UNLINK(resp->items, ev, ev_link);
+ ISC_EVENT_INIT(ev, sizeof(*ev), 0, NULL, DNS_EVENT_DISPATCH,
+ resp->action, resp->arg, resp, NULL, NULL);
+ request_log(disp, resp, LVL(90),
+ "[c] Sent event %p buffer %p len %d to task %p",
+ ev, ev->buffer.base, ev->buffer.length,
+ resp->task);
+ resp->item_out = true;
+ isc_task_send(resp->task, ISC_EVENT_PTR(&ev));
+ }
+ UNLOCK(&disp->lock);
+ return (ISC_R_SUCCESS);
+}
+
+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;
+ bool 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--;
+ dec_stats(disp->mgr, (qid == disp->mgr->qid) ?
+ dns_resstatscounter_disprequdp :
+ dns_resstatscounter_dispreqtcp);
+ INSIST(disp->refcount > 0);
+ disp->refcount--;
+ 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 == true);
+ res->item_out = false;
+ if (ev->buffer.base != NULL)
+ free_buffer(disp, ev->buffer.base, ev->buffer.length);
+ free_devent(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 responses 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_devent(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 = 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(event != NULL);
+
+ if ((disp->attributes & DNS_DISPATCHATTR_NOLISTEN) == 0)
+ return;
+
+ 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;
+ }
+ memmove(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));
+}
+
+dns_dispatch_t *
+dns_dispatchset_get(dns_dispatchset_t *dset) {
+ dns_dispatch_t *disp;
+
+ /* check that dispatch set is configured */
+ if (dset == NULL || dset->ndisp == 0)
+ return (NULL);
+
+ LOCK(&dset->lock);
+ disp = dset->dispatches[dset->cur];
+ dset->cur++;
+ if (dset->cur == dset->ndisp)
+ dset->cur = 0;
+ UNLOCK(&dset->lock);
+
+ return (disp);
+}
+
+isc_result_t
+dns_dispatchset_create(isc_mem_t *mctx, isc_socketmgr_t *sockmgr,
+ isc_taskmgr_t *taskmgr, dns_dispatch_t *source,
+ dns_dispatchset_t **dsetp, int n)
+{
+ isc_result_t result;
+ dns_dispatchset_t *dset;
+ dns_dispatchmgr_t *mgr;
+ int i, j;
+
+ REQUIRE(VALID_DISPATCH(source));
+ REQUIRE((source->attributes & DNS_DISPATCHATTR_UDP) != 0);
+ REQUIRE(dsetp != NULL && *dsetp == NULL);
+
+ mgr = source->mgr;
+
+ dset = isc_mem_get(mctx, sizeof(dns_dispatchset_t));
+ if (dset == NULL)
+ return (ISC_R_NOMEMORY);
+ memset(dset, 0, sizeof(*dset));
+
+ result = isc_mutex_init(&dset->lock);
+ if (result != ISC_R_SUCCESS)
+ goto fail_alloc;
+
+ dset->dispatches = isc_mem_get(mctx, sizeof(dns_dispatch_t *) * n);
+ if (dset->dispatches == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto fail_lock;
+ }
+
+ isc_mem_attach(mctx, &dset->mctx);
+ dset->ndisp = n;
+ dset->cur = 0;
+
+ dset->dispatches[0] = NULL;
+ dns_dispatch_attach(source, &dset->dispatches[0]);
+
+ LOCK(&mgr->lock);
+ for (i = 1; i < n; i++) {
+ dset->dispatches[i] = NULL;
+ result = dispatch_createudp(mgr, sockmgr, taskmgr,
+ &source->local,
+ source->maxrequests,
+ source->attributes,
+ &dset->dispatches[i],
+ source->socket);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+ }
+
+ UNLOCK(&mgr->lock);
+ *dsetp = dset;
+
+ return (ISC_R_SUCCESS);
+
+ fail:
+ UNLOCK(&mgr->lock);
+
+ for (j = 0; j < i; j++)
+ dns_dispatch_detach(&(dset->dispatches[j]));
+ isc_mem_put(mctx, dset->dispatches, sizeof(dns_dispatch_t *) * n);
+ if (dset->mctx == mctx)
+ isc_mem_detach(&dset->mctx);
+
+ fail_lock:
+ DESTROYLOCK(&dset->lock);
+
+ fail_alloc:
+ isc_mem_put(mctx, dset, sizeof(dns_dispatchset_t));
+ return (result);
+}
+
+void
+dns_dispatchset_cancelall(dns_dispatchset_t *dset, isc_task_t *task) {
+ int i;
+
+ REQUIRE(dset != NULL);
+
+ for (i = 0; i < dset->ndisp; i++) {
+ isc_socket_t *sock;
+ sock = dns_dispatch_getsocket(dset->dispatches[i]);
+ isc_socket_cancel(sock, task, ISC_SOCKCANCEL_ALL);
+ }
+}
+
+void
+dns_dispatchset_destroy(dns_dispatchset_t **dsetp) {
+ dns_dispatchset_t *dset;
+ int i;
+
+ REQUIRE(dsetp != NULL && *dsetp != NULL);
+
+ dset = *dsetp;
+ for (i = 0; i < dset->ndisp; i++)
+ dns_dispatch_detach(&(dset->dispatches[i]));
+ isc_mem_put(dset->mctx, dset->dispatches,
+ sizeof(dns_dispatch_t *) * dset->ndisp);
+ DESTROYLOCK(&dset->lock);
+ isc_mem_putanddetach(&dset->mctx, dset, sizeof(dns_dispatchset_t));
+
+ *dsetp = NULL;
+}
+
+void
+dns_dispatch_setdscp(dns_dispatch_t *disp, isc_dscp_t dscp) {
+ REQUIRE(VALID_DISPATCH(disp));
+ disp->dscp = dscp;
+}
+
+isc_dscp_t
+dns_dispatch_getdscp(dns_dispatch_t *disp) {
+ REQUIRE(VALID_DISPATCH(disp));
+ return (disp->dscp);
+}
+
+#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..6c93d97
--- /dev/null
+++ b/lib/dns/dlz.c
@@ -0,0 +1,549 @@
+/*
+ * Portions Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * 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.
+ */
+
+
+/*! \file */
+
+/***
+ *** Imports
+ ***/
+
+#include <config.h>
+
+#include <stdbool.h>
+
+#include <dns/db.h>
+#include <dns/dlz.h>
+#include <dns/fixedname.h>
+#include <dns/log.h>
+#include <dns/master.h>
+#include <dns/ssu.h>
+#include <dns/zone.h>
+
+
+#include <isc/buffer.h>
+#include <isc/commandline.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 = ISC_R_NOTFOUND;
+ dns_dlzallowzonexfr_t allowzonexfr;
+ dns_dlzdb_t *dlzdb;
+
+ /*
+ * Performs checks to make sure data is as we expect it to be.
+ */
+ REQUIRE(name != NULL);
+ REQUIRE(dbp != NULL && *dbp == NULL);
+
+ /*
+ * Find a driver in which the zone exists and transfer is supported
+ */
+ for (dlzdb = ISC_LIST_HEAD(view->dlz_searched);
+ dlzdb != NULL;
+ dlzdb = ISC_LIST_NEXT(dlzdb, link))
+ {
+ REQUIRE(DNS_DLZ_VALID(dlzdb));
+
+ allowzonexfr = dlzdb->implementation->methods->allowzonexfr;
+ result = (*allowzonexfr)(dlzdb->implementation->driverarg,
+ dlzdb->dbdata, dlzdb->mctx,
+ view->rdclass, name, clientaddr, dbp);
+
+ /*
+ * if ISC_R_NOPERM, we found the right database but
+ * the zone may not transfer.
+ */
+ if (result == ISC_R_SUCCESS || result == ISC_R_NOPERM)
+ return (result);
+ }
+
+ if (result == ISC_R_NOTIMPLEMENTED)
+ result = 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;
+ dns_dlzdb_t *db = 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);
+
+ /*
+ * 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 */
+ db = isc_mem_get(mctx, sizeof(dns_dlzdb_t));
+ if (db == NULL) {
+ RWUNLOCK(&dlz_implock, isc_rwlocktype_read);
+ return (ISC_R_NOMEMORY);
+ }
+
+ /* Make sure memory region is set to all 0's */
+ memset(db, 0, sizeof(dns_dlzdb_t));
+
+ ISC_LINK_INIT(db, link);
+ db->implementation = impinfo;
+ if (dlzname != NULL)
+ db->dlzname = isc_mem_strdup(mctx, dlzname);
+
+ /* Create a new database using implementation 'drivername'. */
+ result = ((impinfo->methods->create)(mctx, dlzname, argc, argv,
+ impinfo->driverarg,
+ &db->dbdata));
+
+ /* mark the DLZ driver as valid */
+ if (result == ISC_R_SUCCESS) {
+ RWUNLOCK(&dlz_implock, isc_rwlocktype_read);
+ db->magic = DNS_DLZ_MAGIC;
+ isc_mem_attach(mctx, &db->mctx);
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+ "DLZ driver loaded successfully.");
+ *dbp = db;
+ 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, db, sizeof(dns_dlzdb_t));
+ return (result);
+}
+
+void
+dns_dlzdestroy(dns_dlzdb_t **dbp) {
+ dns_dlzdestroy_t destroy;
+ dns_dlzdb_t *db;
+
+ /* 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));
+
+ db = *dbp;
+ *dbp = NULL;
+
+ if (db->ssutable != NULL)
+ dns_ssutable_detach(&db->ssutable);
+
+ /* call the drivers destroy method */
+ if (db->dlzname != NULL)
+ isc_mem_free(db->mctx, db->dlzname);
+ destroy = db->implementation->methods->destroy;
+ (*destroy)(db->implementation->driverarg, db->dbdata);
+ /* return memory and detach */
+ isc_mem_putanddetach(&db->mctx, db, sizeof(dns_dlzdb_t));
+}
+
+/*%
+ * 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);
+}
+
+/*%
+ * 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(isc_commandline_strtoargv(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);
+}
+
+/*
+ * Create a writeable DLZ zone. This can be called by DLZ drivers
+ * during configure() to create a zone that can be updated. The zone
+ * type is set to dns_zone_dlz, which is equivalent to a master zone
+ *
+ * This function uses a callback setup in dns_dlzconfigure() to call
+ * into the server zone code to setup the remaining pieces of server
+ * specific functionality on the zone
+ */
+isc_result_t
+dns_dlz_writeablezone(dns_view_t *view, dns_dlzdb_t *dlzdb,
+ const char *zone_name)
+{
+ dns_zone_t *zone = NULL;
+ dns_zone_t *dupzone = NULL;
+ isc_result_t result;
+ isc_buffer_t buffer;
+ dns_fixedname_t fixorigin;
+ dns_name_t *origin;
+
+ REQUIRE(DNS_DLZ_VALID(dlzdb));
+
+ REQUIRE(dlzdb->configure_callback != NULL);
+
+ isc_buffer_constinit(&buffer, zone_name, strlen(zone_name));
+ isc_buffer_add(&buffer, strlen(zone_name));
+ dns_fixedname_init(&fixorigin);
+ result = dns_name_fromtext(dns_fixedname_name(&fixorigin),
+ &buffer, dns_rootname, 0, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ origin = dns_fixedname_name(&fixorigin);
+
+ if (!dlzdb->search) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_DLZ, ISC_LOG_WARNING,
+ "DLZ %s has 'search no;', but attempted to "
+ "register writeable zone %s.",
+ dlzdb->dlzname, zone_name);
+ result = ISC_R_SUCCESS;
+ goto cleanup;
+ }
+
+ /* See if the zone already exists */
+ result = dns_view_findzone(view, origin, &dupzone);
+ if (result == ISC_R_SUCCESS) {
+ dns_zone_detach(&dupzone);
+ result = ISC_R_EXISTS;
+ goto cleanup;
+ }
+ INSIST(dupzone == NULL);
+
+ /* Create it */
+ result = dns_zone_create(&zone, view->mctx);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ result = dns_zone_setorigin(zone, origin);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ dns_zone_setview(zone, view);
+
+ dns_zone_setadded(zone, true);
+
+ if (dlzdb->ssutable == NULL) {
+ result = dns_ssutable_createdlz(dlzdb->mctx,
+ &dlzdb->ssutable, dlzdb);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+ dns_zone_setssutable(zone, dlzdb->ssutable);
+
+ result = dlzdb->configure_callback(view, dlzdb, zone);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = dns_view_addzone(view, zone);
+
+
+ cleanup:
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+
+ return (result);
+}
+
+/*%
+ * Configure a DLZ driver. This is optional, and if supplied gives
+ * the backend an opportunity to configure parameters related to DLZ.
+ */
+isc_result_t
+dns_dlzconfigure(dns_view_t *view, dns_dlzdb_t *dlzdb,
+ dlzconfigure_callback_t callback)
+{
+ dns_dlzimplementation_t *impl;
+ isc_result_t result;
+
+ REQUIRE(DNS_DLZ_VALID(dlzdb));
+ REQUIRE(dlzdb->implementation != NULL);
+
+ impl = dlzdb->implementation;
+
+ if (impl->methods->configure == NULL)
+ return (ISC_R_SUCCESS);
+
+ dlzdb->configure_callback = callback;
+
+ result = impl->methods->configure(impl->driverarg, dlzdb->dbdata,
+ view, dlzdb);
+ return (result);
+}
+
+bool
+dns_dlz_ssumatch(dns_dlzdb_t *dlzdatabase, dns_name_t *signer,
+ dns_name_t *name, isc_netaddr_t *tcpaddr,
+ dns_rdatatype_t type, const dst_key_t *key)
+{
+ dns_dlzimplementation_t *impl;
+ bool r;
+
+ REQUIRE(dlzdatabase != NULL);
+ REQUIRE(dlzdatabase->implementation != NULL);
+ REQUIRE(dlzdatabase->implementation->methods != NULL);
+ impl = dlzdatabase->implementation;
+
+ if (impl->methods->ssumatch == NULL) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
+ "No ssumatch method for DLZ database");
+ return (false);
+ }
+
+ r = impl->methods->ssumatch(signer, name, tcpaddr, type, key,
+ impl->driverarg, dlzdatabase->dbdata);
+ return (r);
+}
diff --git a/lib/dns/dns64.c b/lib/dns/dns64.c
new file mode 100644
index 0000000..490445c
--- /dev/null
+++ b/lib/dns/dns64.c
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#include <config.h>
+
+#include <stdbool.h>
+
+#include <isc/list.h>
+#include <isc/mem.h>
+#include <isc/netaddr.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dns/acl.h>
+#include <dns/dns64.h>
+#include <dns/rdata.h>
+#include <dns/rdataset.h>
+#include <dns/result.h>
+
+struct dns_dns64 {
+ unsigned char bits[16]; /*
+ * Prefix + suffix bits.
+ */
+ dns_acl_t * clients; /*
+ * Which clients get mapped
+ * addresses.
+ */
+ dns_acl_t * mapped; /*
+ * IPv4 addresses to be mapped.
+ */
+ dns_acl_t * excluded; /*
+ * IPv6 addresses that are
+ * treated as not existing.
+ */
+ unsigned int prefixlen; /*
+ * Start of mapped address.
+ */
+ unsigned int flags;
+ isc_mem_t * mctx;
+ ISC_LINK(dns_dns64_t) link;
+};
+
+isc_result_t
+dns_dns64_create(isc_mem_t *mctx, isc_netaddr_t *prefix,
+ unsigned int prefixlen, isc_netaddr_t *suffix,
+ dns_acl_t *clients, dns_acl_t *mapped, dns_acl_t *excluded,
+ unsigned int flags, dns_dns64_t **dns64p)
+{
+ dns_dns64_t *dns64;
+ unsigned int nbytes = 16;
+
+ REQUIRE(prefix != NULL && prefix->family == AF_INET6);
+ /* Legal prefix lengths from rfc6052.txt. */
+ REQUIRE(prefixlen == 32 || prefixlen == 40 || prefixlen == 48 ||
+ prefixlen == 56 || prefixlen == 64 || prefixlen == 96);
+ REQUIRE(isc_netaddr_prefixok(prefix, prefixlen) == ISC_R_SUCCESS);
+ REQUIRE(dns64p != NULL && *dns64p == NULL);
+
+ if (suffix != NULL) {
+ static const unsigned char zeros[16];
+ REQUIRE(prefix->family == AF_INET6);
+ nbytes = prefixlen / 8 + 4;
+ /* Bits 64-71 are zeros. rfc6052.txt */
+ if (prefixlen >= 32 && prefixlen <= 64)
+ nbytes++;
+ REQUIRE(memcmp(suffix->type.in6.s6_addr, zeros, nbytes) == 0);
+ }
+
+ dns64 = isc_mem_get(mctx, sizeof(dns_dns64_t));
+ if (dns64 == NULL)
+ return (ISC_R_NOMEMORY);
+ memset(dns64->bits, 0, sizeof(dns64->bits));
+ memmove(dns64->bits, prefix->type.in6.s6_addr, prefixlen / 8);
+ if (suffix != NULL)
+ memmove(dns64->bits + nbytes, suffix->type.in6.s6_addr + nbytes,
+ 16 - nbytes);
+ dns64->clients = NULL;
+ if (clients != NULL)
+ dns_acl_attach(clients, &dns64->clients);
+ dns64->mapped = NULL;
+ if (mapped != NULL)
+ dns_acl_attach(mapped, &dns64->mapped);
+ dns64->excluded = NULL;
+ if (excluded != NULL)
+ dns_acl_attach(excluded, &dns64->excluded);
+ dns64->prefixlen = prefixlen;
+ dns64->flags = flags;
+ ISC_LINK_INIT(dns64, link);
+ dns64->mctx = NULL;
+ isc_mem_attach(mctx, &dns64->mctx);
+ *dns64p = dns64;
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_dns64_destroy(dns_dns64_t **dns64p) {
+ dns_dns64_t *dns64;
+
+ REQUIRE(dns64p != NULL && *dns64p != NULL);
+
+ dns64 = *dns64p;
+ *dns64p = NULL;
+
+ REQUIRE(!ISC_LINK_LINKED(dns64, link));
+
+ if (dns64->clients != NULL)
+ dns_acl_detach(&dns64->clients);
+ if (dns64->mapped != NULL)
+ dns_acl_detach(&dns64->mapped);
+ if (dns64->excluded != NULL)
+ dns_acl_detach(&dns64->excluded);
+ isc_mem_putanddetach(&dns64->mctx, dns64, sizeof(*dns64));
+}
+
+isc_result_t
+dns_dns64_aaaafroma(const dns_dns64_t *dns64, const isc_netaddr_t *reqaddr,
+ const dns_name_t *reqsigner, const dns_aclenv_t *env,
+ unsigned int flags, unsigned char *a, unsigned char *aaaa)
+{
+ unsigned int nbytes, i;
+ isc_result_t result;
+ int match;
+
+ if ((dns64->flags & DNS_DNS64_RECURSIVE_ONLY) != 0 &&
+ (flags & DNS_DNS64_RECURSIVE) == 0)
+ return (DNS_R_DISALLOWED);
+
+ if ((dns64->flags & DNS_DNS64_BREAK_DNSSEC) == 0 &&
+ (flags & DNS_DNS64_DNSSEC) != 0)
+ return (DNS_R_DISALLOWED);
+
+ if (dns64->clients != NULL) {
+ result = dns_acl_match(reqaddr, reqsigner, dns64->clients, env,
+ &match, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ if (match <= 0)
+ return (DNS_R_DISALLOWED);
+ }
+
+ if (dns64->mapped != NULL) {
+ struct in_addr ina;
+ isc_netaddr_t netaddr;
+
+ memmove(&ina.s_addr, a, 4);
+ isc_netaddr_fromin(&netaddr, &ina);
+ result = dns_acl_match(&netaddr, NULL, dns64->mapped, env,
+ &match, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ if (match <= 0)
+ return (DNS_R_DISALLOWED);
+ }
+
+ nbytes = dns64->prefixlen / 8;
+ INSIST(nbytes <= 12);
+ /* Copy prefix. */
+ memmove(aaaa, dns64->bits, nbytes);
+ /* Bits 64-71 are zeros. rfc6052.txt */
+ if (nbytes == 8)
+ aaaa[nbytes++] = 0;
+ /* Copy mapped address. */
+ for (i = 0; i < 4U; i++) {
+ aaaa[nbytes++] = a[i];
+ /* Bits 64-71 are zeros. rfc6052.txt */
+ if (nbytes == 8)
+ aaaa[nbytes++] = 0;
+ }
+ /* Copy suffix. */
+ memmove(aaaa + nbytes, dns64->bits + nbytes, 16 - nbytes);
+ return (ISC_R_SUCCESS);
+}
+
+dns_dns64_t *
+dns_dns64_next(dns_dns64_t *dns64) {
+ dns64 = ISC_LIST_NEXT(dns64, link);
+ return (dns64);
+}
+
+void
+dns_dns64_append(dns_dns64list_t *list, dns_dns64_t *dns64) {
+ ISC_LIST_APPEND(*list, dns64, link);
+}
+
+void
+dns_dns64_unlink(dns_dns64list_t *list, dns_dns64_t *dns64) {
+ ISC_LIST_UNLINK(*list, dns64, link);
+}
+
+bool
+dns_dns64_aaaaok(const dns_dns64_t *dns64, const isc_netaddr_t *reqaddr,
+ const dns_name_t *reqsigner, const dns_aclenv_t *env,
+ unsigned int flags, dns_rdataset_t *rdataset,
+ bool *aaaaok, size_t aaaaoklen)
+{
+ struct in6_addr in6;
+ isc_netaddr_t netaddr;
+ isc_result_t result;
+ int match;
+ bool answer = false;
+ bool found = false;
+ unsigned int i, ok;
+
+ REQUIRE(rdataset != NULL);
+ REQUIRE(rdataset->type == dns_rdatatype_aaaa);
+ REQUIRE(rdataset->rdclass == dns_rdataclass_in);
+ if (aaaaok != NULL)
+ REQUIRE(aaaaoklen == dns_rdataset_count(rdataset));
+
+ for (;dns64 != NULL; dns64 = ISC_LIST_NEXT(dns64, link)) {
+ if ((dns64->flags & DNS_DNS64_RECURSIVE_ONLY) != 0 &&
+ (flags & DNS_DNS64_RECURSIVE) == 0)
+ continue;
+
+ if ((dns64->flags & DNS_DNS64_BREAK_DNSSEC) == 0 &&
+ (flags & DNS_DNS64_DNSSEC) != 0)
+ continue;
+ /*
+ * Work out if this dns64 structure applies to this client.
+ */
+ if (dns64->clients != NULL) {
+ result = dns_acl_match(reqaddr, reqsigner,
+ dns64->clients, env,
+ &match, NULL);
+ if (result != ISC_R_SUCCESS)
+ continue;
+ if (match <= 0)
+ continue;
+ }
+
+ if (!found && aaaaok != NULL) {
+ for (i = 0; i < aaaaoklen; i++)
+ aaaaok[i] = false;
+ }
+ found = true;
+
+ /*
+ * If we are not excluding any addresses then any AAAA
+ * will do.
+ */
+ if (dns64->excluded == NULL) {
+ answer = true;
+ if (aaaaok == NULL)
+ goto done;
+ for (i = 0; i < aaaaoklen; i++)
+ aaaaok[i] = true;
+ goto done;
+ }
+
+ i = 0; ok = 0;
+ for (result = dns_rdataset_first(rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(rdataset)) {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ if (aaaaok == NULL || !aaaaok[i]) {
+
+ dns_rdataset_current(rdataset, &rdata);
+ memmove(&in6.s6_addr, rdata.data, 16);
+ isc_netaddr_fromin6(&netaddr, &in6);
+
+ result = dns_acl_match(&netaddr, NULL,
+ dns64->excluded,
+ env, &match, NULL);
+ if (result == ISC_R_SUCCESS && match <= 0) {
+ answer = true;
+ if (aaaaok == NULL)
+ goto done;
+ aaaaok[i] = true;
+ ok++;
+ }
+ } else
+ ok++;
+ i++;
+ }
+ /*
+ * Are all addresses ok?
+ */
+ if (aaaaok != NULL && ok == aaaaoklen)
+ goto done;
+ }
+
+ done:
+ if (!found && aaaaok != NULL) {
+ for (i = 0; i < aaaaoklen; i++)
+ aaaaok[i] = true;
+ }
+ return (found ? answer : true);
+}
diff --git a/lib/dns/dnssec.c b/lib/dns/dnssec.c
new file mode 100644
index 0000000..7d45e3a
--- /dev/null
+++ b/lib/dns/dnssec.c
@@ -0,0 +1,2241 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include <isc/buffer.h>
+#include <isc/dir.h>
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/serial.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <pk11/site.h>
+
+#include <dns/db.h>
+#include <dns/diff.h>
+#include <dns/dnssec.h>
+#include <dns/fixedname.h>
+#include <dns/keyvalues.h>
+#include <dns/log.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/stats.h>
+#include <dns/tsig.h> /* for DNS_TSIG_FUDGE */
+
+#include <dst/result.h>
+
+LIBDNS_EXTERNAL_DATA isc_stats_t *dns_dnssec_stats;
+
+#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));
+}
+
+static inline void
+inc_stat(isc_statscounter_t counter) {
+ if (dns_dnssec_stats != NULL)
+ isc_stats_increment(dns_dnssec_stats, counter);
+}
+
+/*
+ * 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;
+ dns_rdataset_t rdataset;
+
+ n = dns_rdataset_count(set);
+
+ data = isc_mem_get(mctx, n * sizeof(dns_rdata_t));
+ if (data == NULL)
+ return (ISC_R_NOMEMORY);
+
+ dns_rdataset_init(&rdataset);
+ dns_rdataset_clone(set, &rdataset);
+ ret = dns_rdataset_first(&rdataset);
+ if (ret != ISC_R_SUCCESS) {
+ dns_rdataset_disassociate(&rdataset);
+ 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(&rdataset, &data[i++]);
+ } while (dns_rdataset_next(&rdataset) == ISC_R_SUCCESS);
+
+ /*
+ * Sort the array.
+ */
+ qsort(data, n, sizeof(dns_rdata_t), rdata_compare_wrapper);
+ *rdata = data;
+ *nrdata = n;
+ dns_rdataset_disassociate(&rdataset);
+ 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, bool downcase, dns_rdata_t *sigrdata,
+ dns_rdata_rrsig_t *rrsig)
+{
+ 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);
+ if (downcase) {
+ dns_fixedname_init(&fname);
+
+ RUNTIME_CHECK(dns_name_downcase(&rrsig->signer,
+ dns_fixedname_name(&fname),
+ NULL) == ISC_R_SUCCESS);
+ dns_name_toregion(dns_fixedname_name(&fname), &r);
+ } else
+ dns_name_toregion(&rrsig->signer, &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];
+ uint32_t flags;
+ unsigned int sigsize;
+ dns_fixedname_t fnewname;
+ dns_fixedname_t fsigner;
+
+ 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);
+
+ /*
+ * Downcase signer.
+ */
+ dns_name_init(&sig.signer, NULL);
+ dns_fixedname_init(&fsigner);
+ RUNTIME_CHECK(dns_name_downcase(dst_key_name(key),
+ dns_fixedname_name(&fsigner), NULL) == ISC_R_SUCCESS);
+ dns_name_clone(dns_fixedname_name(&fsigner), &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_create3(key, mctx,
+ DNS_LOGCATEGORY_DNSSEC, true, &ctx);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_databuf;
+
+ /*
+ * Digest the SIG rdata.
+ */
+ ret = digest_sig(ctx, false, &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));
+ memmove(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++) {
+ 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, (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;
+ }
+
+ 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,
+ bool ignoretime, isc_mem_t *mctx,
+ dns_rdata_t *sigrdata, dns_name_t *wild)
+{
+ return (dns_dnssec_verify3(name, set, key, ignoretime, 0, mctx,
+ sigrdata, wild));
+}
+
+isc_result_t
+dns_dnssec_verify3(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
+ bool ignoretime, unsigned int maxbits,
+ 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;
+ uint32_t flags;
+ bool downcase = false;
+
+ 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)) {
+ inc_stat(dns_dnssecstats_fail);
+ return (DNS_R_SIGINVALID);
+ }
+
+ if (!ignoretime) {
+ isc_stdtime_get(&now);
+
+ /*
+ * Is SIG temporally valid?
+ */
+ if (isc_serial_lt((uint32_t)now, sig.timesigned)) {
+ inc_stat(dns_dnssecstats_fail);
+ return (DNS_R_SIGFUTURE);
+ } else if (isc_serial_lt(sig.timeexpire, (uint32_t)now)) {
+ inc_stat(dns_dnssecstats_fail);
+ 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)) {
+ inc_stat(dns_dnssecstats_fail);
+ return (DNS_R_SIGINVALID);
+ }
+ break;
+ case dns_rdatatype_ds:
+ if (dns_name_equal(name, &sig.signer)) {
+ inc_stat(dns_dnssecstats_fail);
+ return (DNS_R_SIGINVALID);
+ }
+ /* FALLTHROUGH */
+ default:
+ if (!dns_name_issubdomain(name, &sig.signer)) {
+ inc_stat(dns_dnssecstats_fail);
+ return (DNS_R_SIGINVALID);
+ }
+ break;
+ }
+
+ /*
+ * Is the key allowed to sign data?
+ */
+ flags = dst_key_flags(key);
+ if (flags & DNS_KEYTYPE_NOAUTH) {
+ inc_stat(dns_dnssecstats_fail);
+ return (DNS_R_KEYUNAUTHORIZED);
+ }
+ if ((flags & DNS_KEYFLAG_OWNERMASK) != DNS_KEYOWNER_ZONE) {
+ inc_stat(dns_dnssecstats_fail);
+ return (DNS_R_KEYUNAUTHORIZED);
+ }
+
+ again:
+ ret = dst_context_create4(key, mctx, DNS_LOGCATEGORY_DNSSEC,
+ false, maxbits, &ctx);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_struct;
+
+ /*
+ * Digest the SIG rdata (not including the signature).
+ */
+ ret = digest_sig(ctx, downcase, 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, '*');
+ memmove(data + 2, r.base, r.length);
+ }
+ else
+ memmove(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++) {
+ 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, (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_verify2(ctx, maxbits, &r);
+ if (ret == ISC_R_SUCCESS && downcase) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ dns_name_format(&sig.signer, namebuf, sizeof(namebuf));
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC,
+ DNS_LOGMODULE_DNSSEC, ISC_LOG_DEBUG(1),
+ "successfully validated after lower casing "
+ "signer '%s'", namebuf);
+ inc_stat(dns_dnssecstats_downcase);
+ } else if (ret == ISC_R_SUCCESS)
+ inc_stat(dns_dnssecstats_asis);
+
+cleanup_array:
+ isc_mem_put(mctx, rdatas, nrdatas * sizeof(dns_rdata_t));
+cleanup_context:
+ dst_context_destroy(&ctx);
+ if (ret == DST_R_VERIFYFAILURE && !downcase) {
+ downcase = true;
+ goto again;
+ }
+cleanup_struct:
+ dns_rdata_freestruct(&sig);
+
+ if (ret == DST_R_VERIFYFAILURE)
+ ret = DNS_R_SIGINVALID;
+
+ if (ret != ISC_R_SUCCESS)
+ inc_stat(dns_dnssecstats_fail);
+
+ 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);
+ inc_stat(dns_dnssecstats_wildcard);
+ ret = DNS_R_FROMWILDCARD;
+ }
+ return (ret);
+}
+
+isc_result_t
+dns_dnssec_verify(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
+ bool 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);
+}
+
+bool
+dns_dnssec_keyactive(dst_key_t *key, isc_stdtime_t now) {
+ isc_result_t result;
+ isc_stdtime_t publish, active, revoke, inactive, deltime;
+ bool pubset = false, actset = false;
+ bool revset = false, inactset = false;
+ bool delset = false;
+ int major, minor;
+
+ /* Is this an old-style key? */
+ result = dst_key_getprivateformat(key, &major, &minor);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ /*
+ * Smart signing started with key format 1.3; prior to that, all
+ * keys are assumed active
+ */
+ if (major == 1 && minor <= 2)
+ return (true);
+
+ result = dst_key_gettime(key, DST_TIME_PUBLISH, &publish);
+ if (result == ISC_R_SUCCESS)
+ pubset = true;
+
+ result = dst_key_gettime(key, DST_TIME_ACTIVATE, &active);
+ if (result == ISC_R_SUCCESS)
+ actset = true;
+
+ result = dst_key_gettime(key, DST_TIME_REVOKE, &revoke);
+ if (result == ISC_R_SUCCESS)
+ revset = true;
+
+ result = dst_key_gettime(key, DST_TIME_INACTIVE, &inactive);
+ if (result == ISC_R_SUCCESS)
+ inactset = true;
+
+ result = dst_key_gettime(key, DST_TIME_DELETE, &deltime);
+ if (result == ISC_R_SUCCESS)
+ delset = true;
+
+ if ((inactset && inactive <= now) || (delset && deltime <= now))
+ return (false);
+
+ if (revset && revoke <= now && pubset && publish <= now)
+ return (true);
+
+ if (actset && active <= now)
+ return (true);
+
+ return (false);
+}
+
+/*%<
+ * Indicate whether a key is scheduled to to have CDS/CDNSKEY records
+ * published now.
+ *
+ * Returns true iff.
+ * - SyncPublish is set and in the past, AND
+ * - SyncDelete is unset or in the future
+ */
+static bool
+syncpublish(dst_key_t *key, isc_stdtime_t now) {
+ isc_result_t result;
+ isc_stdtime_t when;
+ int major, minor;
+
+ /*
+ * Is this an old-style key?
+ */
+ result = dst_key_getprivateformat(key, &major, &minor);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ /*
+ * Smart signing started with key format 1.3
+ */
+ if (major == 1 && minor <= 2)
+ return (false);
+
+ result = dst_key_gettime(key, DST_TIME_SYNCPUBLISH, &when);
+ if (result != ISC_R_SUCCESS)
+ return (false);
+
+ result = dst_key_gettime(key, DST_TIME_SYNCDELETE, &when);
+ if (result != ISC_R_SUCCESS)
+ return (true);
+ if (when <= now)
+ return (false);
+ return (true);
+}
+
+/*%<
+ * Indicate whether a key is scheduled to to have CDS/CDNSKEY records
+ * deleted now.
+ *
+ * Returns true iff. SyncDelete is set and in the past.
+ */
+static bool
+syncdelete(dst_key_t *key, isc_stdtime_t now) {
+ isc_result_t result;
+ isc_stdtime_t when;
+ int major, minor;
+
+ /*
+ * Is this an old-style key?
+ */
+ result = dst_key_getprivateformat(key, &major, &minor);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ /*
+ * Smart signing started with key format 1.3.
+ */
+ if (major == 1 && minor <= 2)
+ return (false);
+
+ result = dst_key_gettime(key, DST_TIME_SYNCDELETE, &when);
+ if (result != ISC_R_SUCCESS)
+ return (false);
+ if (when <= now)
+ return (true);
+ return (false);
+}
+
+#define is_zone_key(key) ((dst_key_flags(key) & DNS_KEYFLAG_OWNERMASK) \
+ == DNS_KEYOWNER_ZONE)
+
+isc_result_t
+dns_dnssec_findzonekeys3(dns_db_t *db, dns_dbversion_t *ver,
+ dns_dbnode_t *node, dns_name_t *name,
+ const char *directory, isc_stdtime_t now,
+ 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;
+ memset(keys, 0, sizeof(*keys) * maxkeys);
+ 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));
+ dst_key_setttl(pubkey, rdataset.ttl);
+
+ 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 the key was revoked and the private file
+ * doesn't exist, maybe it was revoked internally
+ * by named. Try loading the unrevoked version.
+ */
+ if (result == ISC_R_FILENOTFOUND) {
+ uint32_t flags;
+ flags = dst_key_flags(pubkey);
+ if ((flags & DNS_KEYFLAG_REVOKE) != 0) {
+ dst_key_setflags(pubkey,
+ flags & ~DNS_KEYFLAG_REVOKE);
+ 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_SUCCESS &&
+ dst_key_pubcompare(pubkey, keys[count],
+ false)) {
+ dst_key_setflags(keys[count], flags);
+ }
+ dst_key_setflags(pubkey, flags);
+ }
+ }
+
+ if (result != ISC_R_SUCCESS) {
+ char filename[DNS_NAME_FORMATSIZE +
+ DNS_SECALG_FORMATSIZE +
+ sizeof("key file for //65535")];
+ isc_result_t result2;
+ isc_buffer_t buf;
+
+ isc_buffer_init(&buf, filename, ISC_DIR_NAMEMAX);
+ result2 = dst_key_getfilename(dst_key_name(pubkey),
+ dst_key_id(pubkey),
+ dst_key_alg(pubkey),
+ (DST_TYPE_PUBLIC |
+ DST_TYPE_PRIVATE),
+ directory, mctx,
+ &buf);
+ if (result2 != ISC_R_SUCCESS) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char algbuf[DNS_SECALG_FORMATSIZE];
+
+ dns_name_format(dst_key_name(pubkey),
+ namebuf, sizeof(namebuf));
+ dns_secalg_format(dst_key_alg(pubkey),
+ algbuf, sizeof(algbuf));
+ snprintf(filename, sizeof(filename) - 1,
+ "key file for %s/%s/%d",
+ namebuf, algbuf, dst_key_id(pubkey));
+ }
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_DNSSEC, ISC_LOG_WARNING,
+ "dns_dnssec_findzonekeys2: error "
+ "reading %s: %s",
+ filename, isc_result_totext(result));
+ }
+
+ if (result == ISC_R_FILENOTFOUND || result == ISC_R_NOPERM) {
+ keys[count] = pubkey;
+ pubkey = NULL;
+ count++;
+ goto next;
+ }
+
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ /*
+ * If a key is marked inactive, skip it
+ */
+ if (!dns_dnssec_keyactive(keys[count], now)) {
+ dst_key_setinactive(pubkey, true);
+ dst_key_free(&keys[count]);
+ keys[count] = pubkey;
+ pubkey = NULL;
+ count++;
+ goto next;
+ }
+
+ /*
+ * Whatever the key's default TTL may have
+ * been, the rdataset TTL takes priority.
+ */
+ dst_key_setttl(keys[count], rdataset.ttl);
+
+ 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_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)
+{
+ isc_stdtime_t now;
+
+ isc_stdtime_get(&now);
+ return (dns_dnssec_findzonekeys3(db, ver, node, name, directory, now,
+ mctx, maxkeys, keys, nkeys));
+}
+
+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_stdtime_t now;
+
+ isc_stdtime_get(&now);
+ return (dns_dnssec_findzonekeys3(db, ver, node, name, NULL, now,
+ 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;
+ bool signeedsfree = 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_create3(key, mctx,
+ DNS_LOGCATEGORY_DNSSEC, true, &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 = 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) */
+ ISC_LIST_APPEND(datalist->rdata, rdata, link);
+ dataset = NULL;
+ RETERR(dns_message_gettemprdataset(msg, &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;
+ uint16_t addcount, addcount_n;
+ bool signeedsfree = false;
+
+ REQUIRE(source != NULL);
+ REQUIRE(msg != NULL);
+ REQUIRE(key != NULL);
+
+ mctx = msg->mctx;
+
+ msg->verify_attempted = 1;
+ msg->verified_sig = 0;
+ msg->sig0status = dns_tsigerror_badsig;
+
+ 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 = 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((uint32_t)now, sig.timesigned)) {
+ result = DNS_R_SIGFUTURE;
+ msg->sig0status = dns_tsigerror_badtime;
+ goto failure;
+ }
+ else if (isc_serial_lt(sig.timeexpire, (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_create3(key, mctx,
+ DNS_LOGCATEGORY_DNSSEC, false, &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.
+ */
+ memmove(header, source_r.base, DNS_MESSAGE_HEADERLEN);
+
+ /*
+ * Decrement the additional field counter.
+ */
+ memmove(&addcount, &header[DNS_MESSAGE_HEADERLEN - 2], 2);
+ addcount_n = ntohs(addcount);
+ addcount = htons((uint16_t)(addcount_n - 1));
+ memmove(&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;
+ msg->sig0status = dns_rcode_noerror;
+
+ 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);
+}
+
+/*%
+ * Does this key ('rdata') self sign the rrset ('rdataset')?
+ */
+bool
+dns_dnssec_selfsigns(dns_rdata_t *rdata, dns_name_t *name,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset,
+ bool ignoretime, isc_mem_t *mctx)
+{
+ INSIST(rdataset->type == dns_rdatatype_key ||
+ rdataset->type == dns_rdatatype_dnskey);
+ if (rdataset->type == dns_rdatatype_key) {
+ INSIST(sigrdataset->type == dns_rdatatype_sig);
+ INSIST(sigrdataset->covers == dns_rdatatype_key);
+ } else {
+ INSIST(sigrdataset->type == dns_rdatatype_rrsig);
+ INSIST(sigrdataset->covers == dns_rdatatype_dnskey);
+ }
+
+ return (dns_dnssec_signs(rdata, name, rdataset, sigrdataset,
+ ignoretime, mctx));
+
+}
+
+bool
+dns_dnssec_signs(dns_rdata_t *rdata, dns_name_t *name,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset,
+ bool ignoretime, isc_mem_t *mctx)
+{
+ dst_key_t *dstkey = NULL;
+ dns_keytag_t keytag;
+ dns_rdata_dnskey_t key;
+ dns_rdata_rrsig_t sig;
+ dns_rdata_t sigrdata = DNS_RDATA_INIT;
+ isc_result_t result;
+
+ INSIST(sigrdataset->type == dns_rdatatype_rrsig);
+ if (sigrdataset->covers != rdataset->type)
+ return (false);
+
+ result = dns_dnssec_keyfromrdata(name, rdata, mctx, &dstkey);
+ if (result != ISC_R_SUCCESS)
+ return (false);
+ result = dns_rdata_tostruct(rdata, &key, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ keytag = dst_key_id(dstkey);
+ 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) {
+ result = dns_dnssec_verify2(name, rdataset, dstkey,
+ ignoretime, mctx,
+ &sigrdata, NULL);
+ if (result == ISC_R_SUCCESS) {
+ dst_key_free(&dstkey);
+ return (true);
+ }
+ }
+ }
+ dst_key_free(&dstkey);
+ return (false);
+}
+
+isc_result_t
+dns_dnsseckey_create(isc_mem_t *mctx, dst_key_t **dstkey,
+ dns_dnsseckey_t **dkp)
+{
+ isc_result_t result;
+ dns_dnsseckey_t *dk;
+ int major, minor;
+
+ REQUIRE(dkp != NULL && *dkp == NULL);
+ dk = isc_mem_get(mctx, sizeof(dns_dnsseckey_t));
+ if (dk == NULL)
+ return (ISC_R_NOMEMORY);
+
+ dk->key = *dstkey;
+ *dstkey = NULL;
+ dk->force_publish = false;
+ dk->force_sign = false;
+ dk->hint_publish = false;
+ dk->hint_sign = false;
+ dk->hint_remove = false;
+ dk->first_sign = false;
+ dk->is_active = false;
+ dk->prepublish = 0;
+ dk->source = dns_keysource_unknown;
+ dk->index = 0;
+
+ /* KSK or ZSK? */
+ dk->ksk = (dst_key_flags(dk->key) & DNS_KEYFLAG_KSK);
+
+ /* Is this an old-style key? */
+ result = dst_key_getprivateformat(dk->key, &major, &minor);
+ INSIST(result == ISC_R_SUCCESS);
+
+ /* Smart signing started with key format 1.3 */
+ dk->legacy = (major == 1 && minor <= 2);
+
+ ISC_LINK_INIT(dk, link);
+ *dkp = dk;
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_dnsseckey_destroy(isc_mem_t *mctx, dns_dnsseckey_t **dkp) {
+ dns_dnsseckey_t *dk;
+
+ REQUIRE(dkp != NULL && *dkp != NULL);
+ dk = *dkp;
+ if (dk->key != NULL)
+ dst_key_free(&dk->key);
+ isc_mem_put(mctx, dk, sizeof(dns_dnsseckey_t));
+ *dkp = NULL;
+}
+
+static void
+get_hints(dns_dnsseckey_t *key, isc_stdtime_t now) {
+ isc_result_t result;
+ isc_stdtime_t publish, active, revoke, inactive, deltime;
+ bool pubset = false, actset = false;
+ bool revset = false, inactset = false;
+ bool delset = false;
+
+ REQUIRE(key != NULL && key->key != NULL);
+
+ result = dst_key_gettime(key->key, DST_TIME_PUBLISH, &publish);
+ if (result == ISC_R_SUCCESS)
+ pubset = true;
+
+ result = dst_key_gettime(key->key, DST_TIME_ACTIVATE, &active);
+ if (result == ISC_R_SUCCESS)
+ actset = true;
+
+ result = dst_key_gettime(key->key, DST_TIME_REVOKE, &revoke);
+ if (result == ISC_R_SUCCESS)
+ revset = true;
+
+ result = dst_key_gettime(key->key, DST_TIME_INACTIVE, &inactive);
+ if (result == ISC_R_SUCCESS)
+ inactset = true;
+
+ result = dst_key_gettime(key->key, DST_TIME_DELETE, &deltime);
+ if (result == ISC_R_SUCCESS)
+ delset = true;
+
+ /* Metadata says publish (but possibly not activate) */
+ if (pubset && publish <= now)
+ key->hint_publish = true;
+
+ /* Metadata says activate (so we must also publish) */
+ if (actset && active <= now) {
+ key->hint_sign = true;
+
+ /* Only publish if publish time has already passed. */
+ if (pubset && publish <= now)
+ key->hint_publish = true;
+ }
+
+ /*
+ * Activation date is set (maybe in the future), but
+ * publication date isn't. Most likely the user wants to
+ * publish now and activate later.
+ */
+ if (actset && !pubset)
+ key->hint_publish = true;
+
+ /*
+ * If activation date is in the future, make note of how far off
+ */
+ if (key->hint_publish && actset && active > now) {
+ key->prepublish = active - now;
+ }
+
+ /*
+ * Key has been marked inactive: we can continue publishing,
+ * but don't sign.
+ */
+ if (key->hint_publish && inactset && inactive <= now) {
+ key->hint_sign = false;
+ }
+
+ /*
+ * Metadata says revoke. If the key is published,
+ * we *have to* sign with it per RFC5011--even if it was
+ * not active before.
+ *
+ * If it hasn't already been done, we should also revoke it now.
+ */
+ if (key->hint_publish && (revset && revoke <= now)) {
+ uint32_t flags;
+ key->hint_sign = true;
+ flags = dst_key_flags(key->key);
+ if ((flags & DNS_KEYFLAG_REVOKE) == 0) {
+ flags |= DNS_KEYFLAG_REVOKE;
+ dst_key_setflags(key->key, flags);
+ }
+ }
+
+ /*
+ * Metadata says delete, so don't publish this key or sign with it.
+ */
+ if (delset && deltime <= now) {
+ key->hint_publish = false;
+ key->hint_sign = false;
+ key->hint_remove = true;
+ }
+}
+
+/*%
+ * Get a list of DNSSEC keys from the key repository
+ */
+isc_result_t
+dns_dnssec_findmatchingkeys2(dns_name_t *origin, const char *directory,
+ isc_stdtime_t now, isc_mem_t *mctx,
+ dns_dnsseckeylist_t *keylist)
+{
+ isc_result_t result = ISC_R_SUCCESS;
+ bool dir_open = false;
+ dns_dnsseckeylist_t list;
+ isc_dir_t dir;
+ dns_dnsseckey_t *key = NULL;
+ dst_key_t *dstkey = NULL;
+ char namebuf[DNS_NAME_FORMATSIZE];
+ isc_buffer_t b;
+ unsigned int len, i, alg;
+
+ REQUIRE(keylist != NULL);
+ ISC_LIST_INIT(list);
+ isc_dir_init(&dir);
+
+ isc_buffer_init(&b, namebuf, sizeof(namebuf) - 1);
+ RETERR(dns_name_tofilenametext(origin, false, &b));
+ len = isc_buffer_usedlength(&b);
+ namebuf[len] = '\0';
+
+ if (directory == NULL)
+ directory = ".";
+ RETERR(isc_dir_open(&dir, directory));
+ dir_open = true;
+
+ while (isc_dir_read(&dir) == ISC_R_SUCCESS) {
+ if (dir.entry.name[0] != 'K' ||
+ dir.entry.length < len + 1 ||
+ dir.entry.name[len + 1] != '+' ||
+ strncasecmp(dir.entry.name + 1, namebuf, len) != 0)
+ continue;
+
+ alg = 0;
+ for (i = len + 1 + 1; i < dir.entry.length ; i++) {
+ if (dir.entry.name[i] < '0' || dir.entry.name[i] > '9')
+ break;
+ alg *= 10;
+ alg += dir.entry.name[i] - '0';
+ }
+
+ /*
+ * Did we not read exactly 3 digits?
+ * Did we overflow?
+ * Did we correctly terminate?
+ */
+ if (i != len + 1 + 1 + 3 || i >= dir.entry.length ||
+ dir.entry.name[i] != '+')
+ continue;
+
+ for (i++ ; i < dir.entry.length ; i++)
+ if (dir.entry.name[i] < '0' || dir.entry.name[i] > '9')
+ break;
+
+ /*
+ * Did we not read exactly 5 more digits?
+ * Did we overflow?
+ * Did we correctly terminate?
+ */
+ if (i != len + 1 + 1 + 3 + 1 + 5 || i >= dir.entry.length ||
+ strcmp(dir.entry.name + i, ".private") != 0)
+ continue;
+
+ dstkey = NULL;
+ result = dst_key_fromnamedfile(dir.entry.name,
+ directory,
+ DST_TYPE_PUBLIC |
+ DST_TYPE_PRIVATE,
+ mctx, &dstkey);
+
+ switch (alg) {
+#ifndef PK11_MD5_DISABLE
+ case DST_ALG_HMACMD5:
+#endif
+ case DST_ALG_HMACSHA1:
+ case DST_ALG_HMACSHA224:
+ case DST_ALG_HMACSHA256:
+ case DST_ALG_HMACSHA384:
+ case DST_ALG_HMACSHA512:
+ if (result == DST_R_BADKEYTYPE)
+ continue;
+ }
+
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(dns_lctx,
+ DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_DNSSEC,
+ ISC_LOG_WARNING,
+ "dns_dnssec_findmatchingkeys: "
+ "error reading key file %s: %s",
+ dir.entry.name,
+ isc_result_totext(result));
+ continue;
+ }
+
+ RETERR(dns_dnsseckey_create(mctx, &dstkey, &key));
+ key->source = dns_keysource_repository;
+ get_hints(key, now);
+
+ if (key->legacy) {
+ dns_dnsseckey_destroy(mctx, &key);
+ } else {
+ ISC_LIST_APPEND(list, key, link);
+ key = NULL;
+ }
+ }
+
+ if (!ISC_LIST_EMPTY(list)) {
+ result = ISC_R_SUCCESS;
+ ISC_LIST_APPENDLIST(*keylist, list, link);
+ } else
+ result = ISC_R_NOTFOUND;
+
+ failure:
+ if (dir_open)
+ isc_dir_close(&dir);
+ INSIST(key == NULL);
+ while ((key = ISC_LIST_HEAD(list)) != NULL) {
+ ISC_LIST_UNLINK(list, key, link);
+ INSIST(key->key != NULL);
+ dst_key_free(&key->key);
+ dns_dnsseckey_destroy(mctx, &key);
+ }
+ if (dstkey != NULL)
+ dst_key_free(&dstkey);
+ return (result);
+}
+
+isc_result_t
+dns_dnssec_findmatchingkeys(dns_name_t *origin, const char *directory,
+ isc_mem_t *mctx, dns_dnsseckeylist_t *keylist)
+{
+ isc_stdtime_t now;
+
+ isc_stdtime_get(&now);
+ return (dns_dnssec_findmatchingkeys2(origin, directory, now, mctx,
+ keylist));
+}
+
+/*%
+ * Add 'newkey' to 'keylist' if it's not already there.
+ *
+ * If 'savekeys' is true, then we need to preserve all
+ * the keys in the keyset, regardless of whether they have
+ * metadata indicating they should be deactivated or removed.
+ */
+static isc_result_t
+addkey(dns_dnsseckeylist_t *keylist, dst_key_t **newkey,
+ bool savekeys, isc_mem_t *mctx)
+{
+ dns_dnsseckey_t *key;
+ isc_result_t result;
+
+ /* Skip duplicates */
+ for (key = ISC_LIST_HEAD(*keylist);
+ key != NULL;
+ key = ISC_LIST_NEXT(key, link)) {
+ if (dst_key_id(key->key) == dst_key_id(*newkey) &&
+ dst_key_alg(key->key) == dst_key_alg(*newkey) &&
+ dns_name_equal(dst_key_name(key->key),
+ dst_key_name(*newkey)))
+ break;
+ }
+
+ if (key != NULL) {
+ /*
+ * Found a match. If the old key was only public and the
+ * new key is private, replace the old one; otherwise
+ * leave it. But either way, mark the key as having
+ * been found in the zone.
+ */
+ if (dst_key_isprivate(key->key)) {
+ dst_key_free(newkey);
+ } else if (dst_key_isprivate(*newkey)) {
+ dst_key_free(&key->key);
+ key->key = *newkey;
+ }
+
+ key->source = dns_keysource_zoneapex;
+ return (ISC_R_SUCCESS);
+ }
+
+ result = dns_dnsseckey_create(mctx, newkey, &key);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ if (key->legacy || savekeys) {
+ key->force_publish = true;
+ key->force_sign = dst_key_isprivate(key->key);
+ }
+ key->source = dns_keysource_zoneapex;
+ ISC_LIST_APPEND(*keylist, key, link);
+ *newkey = NULL;
+ return (ISC_R_SUCCESS);
+}
+
+
+/*%
+ * Mark all keys which signed the DNSKEY/SOA RRsets as "active",
+ * for future reference.
+ */
+static isc_result_t
+mark_active_keys(dns_dnsseckeylist_t *keylist, dns_rdataset_t *rrsigs) {
+ isc_result_t result = ISC_R_SUCCESS;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdataset_t sigs;
+ dns_dnsseckey_t *key;
+
+ REQUIRE(rrsigs != NULL && dns_rdataset_isassociated(rrsigs));
+
+ dns_rdataset_init(&sigs);
+ dns_rdataset_clone(rrsigs, &sigs);
+ for (key = ISC_LIST_HEAD(*keylist);
+ key != NULL;
+ key = ISC_LIST_NEXT(key, link)) {
+ uint16_t keyid, sigid;
+ dns_secalg_t keyalg, sigalg;
+ keyid = dst_key_id(key->key);
+ keyalg = dst_key_alg(key->key);
+
+ for (result = dns_rdataset_first(&sigs);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&sigs)) {
+ dns_rdata_rrsig_t sig;
+
+ dns_rdata_reset(&rdata);
+ dns_rdataset_current(&sigs, &rdata);
+ result = dns_rdata_tostruct(&rdata, &sig, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ sigalg = sig.algorithm;
+ sigid = sig.keyid;
+ if (keyid == sigid && keyalg == sigalg) {
+ key->is_active = true;
+ break;
+ }
+ }
+ }
+
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+
+ if (dns_rdataset_isassociated(&sigs))
+ dns_rdataset_disassociate(&sigs);
+ return (result);
+}
+
+/*%
+ * Add the contents of a DNSKEY rdataset 'keyset' to 'keylist'.
+ */
+isc_result_t
+dns_dnssec_keylistfromrdataset(dns_name_t *origin,
+ const char *directory, isc_mem_t *mctx,
+ dns_rdataset_t *keyset, dns_rdataset_t *keysigs,
+ dns_rdataset_t *soasigs, bool savekeys,
+ bool publickey,
+ dns_dnsseckeylist_t *keylist)
+{
+ dns_rdataset_t keys;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dst_key_t *pubkey = NULL, *privkey = NULL;
+ isc_result_t result;
+
+ REQUIRE(keyset != NULL && dns_rdataset_isassociated(keyset));
+
+ dns_rdataset_init(&keys);
+
+ dns_rdataset_clone(keyset, &keys);
+ for (result = dns_rdataset_first(&keys);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&keys)) {
+ dns_rdata_reset(&rdata);
+ dns_rdataset_current(&keys, &rdata);
+ RETERR(dns_dnssec_keyfromrdata(origin, &rdata, mctx, &pubkey));
+ dst_key_setttl(pubkey, keys.ttl);
+
+ if (!is_zone_key(pubkey) ||
+ (dst_key_flags(pubkey) & DNS_KEYTYPE_NOAUTH) != 0)
+ goto skip;
+
+ /* Corrupted .key file? */
+ if (!dns_name_equal(origin, dst_key_name(pubkey)))
+ goto skip;
+
+ if (publickey) {
+ RETERR(addkey(keylist, &pubkey, savekeys, mctx));
+ goto skip;
+ }
+
+ result = dst_key_fromfile(dst_key_name(pubkey),
+ dst_key_id(pubkey),
+ dst_key_alg(pubkey),
+ DST_TYPE_PUBLIC|DST_TYPE_PRIVATE,
+ directory, mctx, &privkey);
+
+ /*
+ * If the key was revoked and the private file
+ * doesn't exist, maybe it was revoked internally
+ * by named. Try loading the unrevoked version.
+ */
+ if (result == ISC_R_FILENOTFOUND) {
+ uint32_t flags;
+ flags = dst_key_flags(pubkey);
+ if ((flags & DNS_KEYFLAG_REVOKE) != 0) {
+ dst_key_setflags(pubkey,
+ flags & ~DNS_KEYFLAG_REVOKE);
+ result = dst_key_fromfile(dst_key_name(pubkey),
+ dst_key_id(pubkey),
+ dst_key_alg(pubkey),
+ DST_TYPE_PUBLIC|
+ DST_TYPE_PRIVATE,
+ directory,
+ mctx, &privkey);
+ if (result == ISC_R_SUCCESS &&
+ dst_key_pubcompare(pubkey, privkey,
+ false)) {
+ dst_key_setflags(privkey, flags);
+ }
+ dst_key_setflags(pubkey, flags);
+ }
+ }
+
+ if (result != ISC_R_SUCCESS) {
+ char filename[DNS_NAME_FORMATSIZE +
+ DNS_SECALG_FORMATSIZE +
+ sizeof("key file for //65535")];
+ isc_result_t result2;
+ isc_buffer_t buf;
+
+ isc_buffer_init(&buf, filename, ISC_DIR_NAMEMAX);
+ result2 = dst_key_getfilename(dst_key_name(pubkey),
+ dst_key_id(pubkey),
+ dst_key_alg(pubkey),
+ (DST_TYPE_PUBLIC |
+ DST_TYPE_PRIVATE),
+ directory, mctx,
+ &buf);
+ if (result2 != ISC_R_SUCCESS) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char algbuf[DNS_SECALG_FORMATSIZE];
+
+ dns_name_format(dst_key_name(pubkey),
+ namebuf, sizeof(namebuf));
+ dns_secalg_format(dst_key_alg(pubkey),
+ algbuf, sizeof(algbuf));
+ snprintf(filename, sizeof(filename) - 1,
+ "key file for %s/%s/%d",
+ namebuf, algbuf, dst_key_id(pubkey));
+ }
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_DNSSEC, ISC_LOG_WARNING,
+ "dns_dnssec_keylistfromrdataset: error "
+ "reading %s: %s",
+ filename, isc_result_totext(result));
+ }
+
+ if (result == ISC_R_FILENOTFOUND || result == ISC_R_NOPERM) {
+ RETERR(addkey(keylist, &pubkey, savekeys, mctx));
+ goto skip;
+ }
+ RETERR(result);
+
+ /* This should never happen. */
+ if ((dst_key_flags(privkey) & DNS_KEYTYPE_NOAUTH) != 0)
+ goto skip;
+
+ /*
+ * Whatever the key's default TTL may have
+ * been, the rdataset TTL takes priority.
+ */
+ dst_key_setttl(privkey, dst_key_getttl(pubkey));
+
+ RETERR(addkey(keylist, &privkey, savekeys, mctx));
+ skip:
+ if (pubkey != NULL)
+ dst_key_free(&pubkey);
+ if (privkey != NULL)
+ dst_key_free(&privkey);
+ }
+
+ if (result != ISC_R_NOMORE)
+ RETERR(result);
+
+ if (keysigs != NULL && dns_rdataset_isassociated(keysigs))
+ RETERR(mark_active_keys(keylist, keysigs));
+
+ if (soasigs != NULL && dns_rdataset_isassociated(soasigs))
+ RETERR(mark_active_keys(keylist, soasigs));
+
+ result = ISC_R_SUCCESS;
+
+ failure:
+ if (dns_rdataset_isassociated(&keys))
+ dns_rdataset_disassociate(&keys);
+ if (pubkey != NULL)
+ dst_key_free(&pubkey);
+ if (privkey != NULL)
+ dst_key_free(&privkey);
+ return (result);
+}
+
+static isc_result_t
+make_dnskey(dst_key_t *key, unsigned char *buf, int bufsize,
+ dns_rdata_t *target)
+{
+ isc_result_t result;
+ isc_buffer_t b;
+ isc_region_t r;
+
+ isc_buffer_init(&b, buf, bufsize);
+ result = dst_key_todns(key, &b);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ dns_rdata_reset(target);
+ isc_buffer_usedregion(&b, &r);
+ dns_rdata_fromregion(target, dst_key_class(key),
+ dns_rdatatype_dnskey, &r);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+publish(dns_rdata_t *rdata, dns_diff_t *diff, dns_name_t *origin,
+ dns_ttl_t ttl, isc_mem_t *mctx)
+{
+ isc_result_t result;
+ dns_difftuple_t *tuple = NULL;
+
+ RETERR(dns_difftuple_create(mctx, DNS_DIFFOP_ADD, origin, ttl,
+ rdata, &tuple));
+ dns_diff_appendminimal(diff, &tuple);
+
+ failure:
+ return (result);
+}
+
+static isc_result_t
+delrdata(dns_rdata_t *rdata, dns_diff_t *diff, dns_name_t *origin,
+ dns_ttl_t ttl, isc_mem_t *mctx)
+{
+ isc_result_t result;
+ dns_difftuple_t *tuple = NULL;
+
+ RETERR(dns_difftuple_create(mctx, DNS_DIFFOP_DEL, origin, ttl,
+ rdata, &tuple));
+ dns_diff_appendminimal(diff, &tuple);
+
+ failure:
+ return (result);
+}
+
+static isc_result_t
+publish_key(dns_diff_t *diff, dns_dnsseckey_t *key, dns_name_t *origin,
+ dns_ttl_t ttl, isc_mem_t *mctx, bool allzsk,
+ void (*report)(const char *, ...))
+{
+ isc_result_t result;
+ dns_difftuple_t *tuple = NULL;
+ unsigned char buf[DST_KEY_MAXSIZE];
+ dns_rdata_t dnskey = DNS_RDATA_INIT;
+ char alg[80];
+
+ dns_rdata_reset(&dnskey);
+ RETERR(make_dnskey(key->key, buf, sizeof(buf), &dnskey));
+
+ dns_secalg_format(dst_key_alg(key->key), alg, sizeof(alg));
+ report("Fetching %s %d/%s from key %s.",
+ key->ksk ? (allzsk ? "KSK/ZSK" : "KSK") : "ZSK",
+ dst_key_id(key->key), alg,
+ key->source == dns_keysource_user ? "file" : "repository");
+
+ if (key->prepublish && ttl > key->prepublish) {
+ char keystr[DST_KEY_FORMATSIZE];
+ isc_stdtime_t now;
+
+ dst_key_format(key->key, keystr, sizeof(keystr));
+ report("Key %s: Delaying activation to match the DNSKEY TTL.\n",
+ keystr, ttl);
+
+ isc_stdtime_get(&now);
+ dst_key_settime(key->key, DST_TIME_ACTIVATE, now + ttl);
+ }
+
+ /* publish key */
+ RETERR(dns_difftuple_create(mctx, DNS_DIFFOP_ADD, origin, ttl,
+ &dnskey, &tuple));
+ dns_diff_appendminimal(diff, &tuple);
+ result = ISC_R_SUCCESS;
+
+ failure:
+ return (result);
+}
+
+static isc_result_t
+remove_key(dns_diff_t *diff, dns_dnsseckey_t *key, dns_name_t *origin,
+ dns_ttl_t ttl, isc_mem_t *mctx, const char *reason,
+ void (*report)(const char *, ...))
+{
+ isc_result_t result;
+ dns_difftuple_t *tuple = NULL;
+ unsigned char buf[DST_KEY_MAXSIZE];
+ dns_rdata_t dnskey = DNS_RDATA_INIT;
+ char alg[80];
+
+ dns_secalg_format(dst_key_alg(key->key), alg, sizeof(alg));
+ report("Removing %s key %d/%s from DNSKEY RRset.",
+ reason, dst_key_id(key->key), alg);
+
+ RETERR(make_dnskey(key->key, buf, sizeof(buf), &dnskey));
+ RETERR(dns_difftuple_create(mctx, DNS_DIFFOP_DEL, origin, ttl, &dnskey,
+ &tuple));
+ dns_diff_appendminimal(diff, &tuple);
+ result = ISC_R_SUCCESS;
+
+ failure:
+ return (result);
+}
+
+static bool
+exists(dns_rdataset_t *rdataset, dns_rdata_t *rdata) {
+ isc_result_t result;
+ dns_rdataset_t trdataset;
+
+ dns_rdataset_init(&trdataset);
+ dns_rdataset_clone(rdataset, &trdataset);
+ for (result = dns_rdataset_first(&trdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&trdataset)) {
+ dns_rdata_t current = DNS_RDATA_INIT;
+
+ dns_rdataset_current(&trdataset, &current);
+ if (dns_rdata_compare(rdata, &current) == 0) {
+ dns_rdataset_disassociate(&trdataset);
+ return (true);
+ }
+ }
+ dns_rdataset_disassociate(&trdataset);
+ return (false);
+}
+
+isc_result_t
+dns_dnssec_syncupdate(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *rmkeys,
+ dns_rdataset_t *cds, dns_rdataset_t *cdnskey,
+ isc_stdtime_t now, dns_ttl_t ttl, dns_diff_t *diff,
+ isc_mem_t *mctx)
+{
+ unsigned char dsbuf1[DNS_DS_BUFFERSIZE];
+ unsigned char dsbuf2[DNS_DS_BUFFERSIZE];
+ unsigned char keybuf[DST_KEY_MAXSIZE];
+ isc_result_t result;
+ dns_dnsseckey_t *key;
+
+ for (key = ISC_LIST_HEAD(*keys);
+ key != NULL;
+ key = ISC_LIST_NEXT(key, link)) {
+ dns_rdata_t cdsrdata1 = DNS_RDATA_INIT;
+ dns_rdata_t cdsrdata2 = DNS_RDATA_INIT;
+ dns_rdata_t cdnskeyrdata = DNS_RDATA_INIT;
+ dns_name_t *origin = dst_key_name(key->key);
+
+ RETERR(make_dnskey(key->key, keybuf, sizeof(keybuf),
+ &cdnskeyrdata));
+
+ /*
+ * XXXMPA we need to be able to specify the DS algorithms
+ * to be used here and below with rmkeys.
+ */
+ RETERR(dns_ds_buildrdata(origin, &cdnskeyrdata,
+ DNS_DSDIGEST_SHA1, dsbuf1,
+ &cdsrdata1));
+ RETERR(dns_ds_buildrdata(origin, &cdnskeyrdata,
+ DNS_DSDIGEST_SHA256, dsbuf2,
+ &cdsrdata2));
+
+ /*
+ * Now that the we have created the DS records convert
+ * the rdata to CDNSKEY and CDS for comparison.
+ */
+ cdnskeyrdata.type = dns_rdatatype_cdnskey;
+ cdsrdata1.type = dns_rdatatype_cds;
+ cdsrdata2.type = dns_rdatatype_cds;
+
+ if (syncpublish(key->key, now)) {
+ if (!dns_rdataset_isassociated(cdnskey) ||
+ !exists(cdnskey, &cdnskeyrdata))
+ RETERR(publish(&cdnskeyrdata, diff, origin,
+ ttl, mctx));
+ if (!dns_rdataset_isassociated(cds) ||
+ !exists(cds, &cdsrdata1))
+ RETERR(publish(&cdsrdata1, diff, origin,
+ ttl, mctx));
+ if (!dns_rdataset_isassociated(cds) ||
+ !exists(cds, &cdsrdata2))
+ RETERR(publish(&cdsrdata2, diff, origin,
+ ttl, mctx));
+ }
+
+ if (dns_rdataset_isassociated(cds) &&
+ syncdelete(key->key, now)) {
+ if (exists(cds, &cdsrdata1))
+ RETERR(delrdata(&cdsrdata1, diff, origin,
+ cds->ttl, mctx));
+ if (exists(cds, &cdsrdata2))
+ RETERR(delrdata(&cdsrdata2, diff, origin,
+ cds->ttl, mctx));
+ }
+
+ if (dns_rdataset_isassociated(cdnskey) &&
+ syncdelete(key->key, now)) {
+ if (exists(cdnskey, &cdnskeyrdata))
+ RETERR(delrdata(&cdnskeyrdata, diff, origin,
+ cdnskey->ttl, mctx));
+ }
+ }
+
+ if (!dns_rdataset_isassociated(cds) &&
+ !dns_rdataset_isassociated(cdnskey))
+ return (ISC_R_SUCCESS);
+
+ /*
+ * Unconditionaly remove CDS/DNSKEY records for removed keys.
+ */
+ for (key = ISC_LIST_HEAD(*rmkeys);
+ key != NULL;
+ key = ISC_LIST_NEXT(key, link)) {
+ dns_rdata_t cdsrdata1 = DNS_RDATA_INIT;
+ dns_rdata_t cdsrdata2 = DNS_RDATA_INIT;
+ dns_rdata_t cdnskeyrdata = DNS_RDATA_INIT;
+ dns_name_t *origin = dst_key_name(key->key);
+
+ RETERR(make_dnskey(key->key, keybuf, sizeof(keybuf),
+ &cdnskeyrdata));
+
+ if (dns_rdataset_isassociated(cds)) {
+ RETERR(dns_ds_buildrdata(origin, &cdnskeyrdata,
+ DNS_DSDIGEST_SHA1, dsbuf1,
+ &cdsrdata1));
+ RETERR(dns_ds_buildrdata(origin, &cdnskeyrdata,
+ DNS_DSDIGEST_SHA256, dsbuf2,
+ &cdsrdata2));
+ if (exists(cds, &cdsrdata1))
+ RETERR(delrdata(&cdsrdata1, diff, origin,
+ cds->ttl, mctx));
+ if (exists(cds, &cdsrdata2))
+ RETERR(delrdata(&cdsrdata2, diff, origin,
+ cds->ttl, mctx));
+ }
+
+ if (dns_rdataset_isassociated(cdnskey)) {
+ if (exists(cdnskey, &cdnskeyrdata))
+ RETERR(delrdata(&cdnskeyrdata, diff, origin,
+ cdnskey->ttl, mctx));
+ }
+ }
+
+ result = ISC_R_SUCCESS;
+
+ failure:
+ return (result);
+}
+
+/*
+ * Update 'keys' with information from 'newkeys'.
+ *
+ * If 'removed' is not NULL, any keys that are being removed from
+ * the zone will be added to the list for post-removal processing.
+ */
+isc_result_t
+dns_dnssec_updatekeys(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *newkeys,
+ dns_dnsseckeylist_t *removed, dns_name_t *origin,
+ dns_ttl_t hint_ttl, dns_diff_t *diff,
+ bool allzsk, isc_mem_t *mctx,
+ void (*report)(const char *, ...))
+{
+ isc_result_t result;
+ dns_dnsseckey_t *key, *key1, *key2, *next;
+ bool found_ttl = false;
+ dns_ttl_t ttl = hint_ttl;
+
+ /*
+ * First, look through the existing key list to find keys
+ * supplied from the command line which are not in the zone.
+ * Update the zone to include them.
+ *
+ * Also, if there are keys published in the zone already,
+ * use their TTL for all subsequent published keys.
+ */
+ for (key = ISC_LIST_HEAD(*keys);
+ key != NULL;
+ key = ISC_LIST_NEXT(key, link)) {
+ if (key->source == dns_keysource_user &&
+ (key->hint_publish || key->force_publish)) {
+ RETERR(publish_key(diff, key, origin, ttl,
+ mctx, allzsk, report));
+ }
+ if (key->source == dns_keysource_zoneapex) {
+ ttl = dst_key_getttl(key->key);
+ found_ttl = true;
+ }
+ }
+
+ /*
+ * If there were no existing keys, use the smallest nonzero
+ * TTL of the keys found in the repository.
+ */
+ if (!found_ttl && !ISC_LIST_EMPTY(*newkeys)) {
+ dns_ttl_t shortest = 0;
+
+ for (key = ISC_LIST_HEAD(*newkeys);
+ key != NULL;
+ key = ISC_LIST_NEXT(key, link)) {
+ dns_ttl_t thisttl = dst_key_getttl(key->key);
+ if (thisttl != 0 &&
+ (shortest == 0 || thisttl < shortest))
+ shortest = thisttl;
+ }
+
+ if (shortest != 0)
+ ttl = shortest;
+ }
+
+ /*
+ * Second, scan the list of newly found keys looking for matches
+ * with known keys, and update accordingly.
+ */
+ for (key1 = ISC_LIST_HEAD(*newkeys); key1 != NULL; key1 = next) {
+ bool key_revoked = false;
+
+ next = ISC_LIST_NEXT(key1, link);
+
+ for (key2 = ISC_LIST_HEAD(*keys);
+ key2 != NULL;
+ key2 = ISC_LIST_NEXT(key2, link)) {
+ int f1 = dst_key_flags(key1->key);
+ int f2 = dst_key_flags(key2->key);
+ int nr1 = f1 & ~DNS_KEYFLAG_REVOKE;
+ int nr2 = f2 & ~DNS_KEYFLAG_REVOKE;
+ if (nr1 == nr2 &&
+ dst_key_alg(key1->key) == dst_key_alg(key2->key) &&
+ dst_key_pubcompare(key1->key, key2->key,
+ true)) {
+ int r1, r2;
+ r1 = dst_key_flags(key1->key) &
+ DNS_KEYFLAG_REVOKE;
+ r2 = dst_key_flags(key2->key) &
+ DNS_KEYFLAG_REVOKE;
+ key_revoked = (r1 != r2);
+ break;
+ }
+ }
+
+ /* No match found in keys; add the new key. */
+ if (key2 == NULL) {
+ ISC_LIST_UNLINK(*newkeys, key1, link);
+ ISC_LIST_APPEND(*keys, key1, link);
+
+ if (key1->source != dns_keysource_zoneapex &&
+ (key1->hint_publish || key1->force_publish)) {
+ RETERR(publish_key(diff, key1, origin, ttl,
+ mctx, allzsk, report));
+ if (key1->hint_sign || key1->force_sign)
+ key1->first_sign = true;
+ }
+
+ continue;
+ }
+
+ /* Match found: remove or update it as needed */
+ if (key1->hint_remove) {
+ RETERR(remove_key(diff, key2, origin, ttl, mctx,
+ "expired", report));
+ ISC_LIST_UNLINK(*keys, key2, link);
+ if (removed != NULL)
+ ISC_LIST_APPEND(*removed, key2, link);
+ else
+ dns_dnsseckey_destroy(mctx, &key2);
+ } else if (key_revoked &&
+ (dst_key_flags(key1->key) & DNS_KEYFLAG_REVOKE) != 0) {
+
+ /*
+ * A previously valid key has been revoked.
+ * We need to remove the old version and pull
+ * in the new one.
+ */
+ RETERR(remove_key(diff, key2, origin, ttl, mctx,
+ "revoked", report));
+ ISC_LIST_UNLINK(*keys, key2, link);
+ if (removed != NULL)
+ ISC_LIST_APPEND(*removed, key2, link);
+ else
+ dns_dnsseckey_destroy(mctx, &key2);
+
+ RETERR(publish_key(diff, key1, origin, ttl,
+ mctx, allzsk, report));
+ ISC_LIST_UNLINK(*newkeys, key1, link);
+ ISC_LIST_APPEND(*keys, key1, link);
+
+ /*
+ * XXX: The revoke flag is only defined for trust
+ * anchors. Setting the flag on a non-KSK is legal,
+ * but not defined in any RFC. It seems reasonable
+ * to treat it the same as a KSK: keep it in the
+ * zone, sign the DNSKEY set with it, but not
+ * sign other records with it.
+ */
+ key1->ksk = true;
+ continue;
+ } else {
+ if (!key2->is_active &&
+ (key1->hint_sign || key1->force_sign))
+ key2->first_sign = true;
+ key2->hint_sign = key1->hint_sign;
+ key2->hint_publish = key1->hint_publish;
+ }
+ }
+
+ /* Free any leftover keys in newkeys */
+ while (!ISC_LIST_EMPTY(*newkeys)) {
+ key1 = ISC_LIST_HEAD(*newkeys);
+ ISC_LIST_UNLINK(*newkeys, key1, link);
+ dns_dnsseckey_destroy(mctx, &key1);
+ }
+
+ result = ISC_R_SUCCESS;
+
+ failure:
+ return (result);
+}
diff --git a/lib/dns/dnstap.c b/lib/dns/dnstap.c
new file mode 100644
index 0000000..ab3bd74
--- /dev/null
+++ b/lib/dns/dnstap.c
@@ -0,0 +1,1251 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * Copyright (c) 2013-2014, Farsight Security, Inc.
+ * 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 copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#ifndef HAVE_DNSTAP
+#error DNSTAP not configured.
+#endif /* HAVE_DNSTAP */
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include <isc/buffer.h>
+#include <isc/file.h>
+#include <isc/mem.h>
+#include <isc/once.h>
+#include <isc/print.h>
+#include <isc/sockaddr.h>
+#include <isc/thread.h>
+#include <isc/time.h>
+#include <isc/types.h>
+#include <isc/util.h>
+
+#include <dns/dnstap.h>
+#include <dns/log.h>
+#include <dns/message.h>
+#include <dns/name.h>
+#include <dns/rdataset.h>
+#include <dns/result.h>
+#include <dns/stats.h>
+#include <dns/types.h>
+#include <dns/view.h>
+
+#include <dns/dnstap.pb-c.h>
+#include <protobuf-c/protobuf-c.h>
+
+#define DTENV_MAGIC ISC_MAGIC('D', 't', 'n', 'v')
+#define VALID_DTENV(env) ISC_MAGIC_VALID(env, DTENV_MAGIC)
+
+#define DNSTAP_CONTENT_TYPE "protobuf:dnstap.Dnstap"
+#define DNSTAP_INITIAL_BUF_SIZE 256
+
+struct dns_dtmsg {
+ void *buf;
+ size_t len;
+ Dnstap__Dnstap d;
+ Dnstap__Message m;
+};
+
+struct dns_dthandle {
+ dns_dtmode_t mode;
+ struct fstrm_reader *reader;
+ isc_mem_t *mctx;
+};
+
+struct dns_dtenv {
+ unsigned int magic;
+ isc_refcount_t refcount;
+
+ isc_mem_t *mctx;
+
+ struct fstrm_iothr *iothr;
+ struct fstrm_iothr_options *fopt;
+
+ isc_region_t identity;
+ isc_region_t version;
+ char *path;
+ dns_dtmode_t mode;
+ isc_stats_t *stats;
+};
+
+#define CHECK(x) do { \
+ result = (x); \
+ if (result != ISC_R_SUCCESS) \
+ goto cleanup; \
+ } while (0)
+
+static isc_mutex_t dt_mutex;
+static bool dt_initialized = false;
+static isc_thread_key_t dt_key;
+static isc_once_t mutex_once = ISC_ONCE_INIT;
+static isc_mem_t *dt_mctx = NULL;
+
+/*
+ * Change under task exclusive.
+ */
+static unsigned int generation;
+
+static void
+mutex_init(void) {
+ RUNTIME_CHECK(isc_mutex_init(&dt_mutex) == ISC_R_SUCCESS);
+}
+
+static void
+dtfree(void *arg) {
+ free(arg);
+ isc_thread_key_setspecific(dt_key, NULL);
+}
+
+static isc_result_t
+dt_init(void) {
+ isc_result_t result;
+
+ result = isc_once_do(&mutex_once, mutex_init);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ if (dt_initialized)
+ return (ISC_R_SUCCESS);
+
+ LOCK(&dt_mutex);
+ if (!dt_initialized) {
+ int ret;
+
+ if (dt_mctx == NULL)
+ result = isc_mem_create2(0, 0, &dt_mctx, 0);
+ if (result != ISC_R_SUCCESS)
+ goto unlock;
+ isc_mem_setname(dt_mctx, "dt", NULL);
+ isc_mem_setdestroycheck(dt_mctx, false);
+
+ ret = isc_thread_key_create(&dt_key, dtfree);
+ if (ret == 0)
+ dt_initialized = true;
+ else
+ result = ISC_R_FAILURE;
+ }
+unlock:
+ UNLOCK(&dt_mutex);
+
+ return (result);
+}
+
+isc_result_t
+dns_dt_create(isc_mem_t *mctx, dns_dtmode_t mode, const char *path,
+ struct fstrm_iothr_options **foptp, dns_dtenv_t **envp)
+{
+ isc_result_t result = ISC_R_SUCCESS;
+ fstrm_res res;
+ struct fstrm_unix_writer_options *fuwopt = NULL;
+ struct fstrm_file_options *ffwopt = NULL;
+ struct fstrm_writer_options *fwopt = NULL;
+ struct fstrm_writer *fw = NULL;
+ dns_dtenv_t *env = NULL;
+
+ REQUIRE(path != NULL);
+ REQUIRE(envp != NULL && *envp == NULL);
+ REQUIRE(foptp!= NULL && *foptp != NULL);
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSTAP,
+ DNS_LOGMODULE_DNSTAP, ISC_LOG_INFO,
+ "opening dnstap destination '%s'", path);
+
+ generation++;
+
+ env = isc_mem_get(mctx, sizeof(dns_dtenv_t));
+ if (env == NULL)
+ CHECK(ISC_R_NOMEMORY);
+
+ memset(env, 0, sizeof(dns_dtenv_t));
+
+ CHECK(isc_refcount_init(&env->refcount, 1));
+ CHECK(isc_stats_create(mctx, &env->stats, dns_dnstapcounter_max));
+ env->path = isc_mem_strdup(mctx, path);
+ if (env->path == NULL)
+ CHECK(ISC_R_NOMEMORY);
+
+ fwopt = fstrm_writer_options_init();
+ if (fwopt == NULL)
+ CHECK(ISC_R_NOMEMORY);
+
+ res = fstrm_writer_options_add_content_type(fwopt,
+ DNSTAP_CONTENT_TYPE,
+ sizeof(DNSTAP_CONTENT_TYPE) - 1);
+ if (res != fstrm_res_success)
+ CHECK(ISC_R_FAILURE);
+
+ if (mode == dns_dtmode_file) {
+ ffwopt = fstrm_file_options_init();
+ if (ffwopt != NULL) {
+ fstrm_file_options_set_file_path(ffwopt, env->path);
+ fw = fstrm_file_writer_init(ffwopt, fwopt);
+ }
+ } else if (mode == dns_dtmode_unix) {
+ fuwopt = fstrm_unix_writer_options_init();
+ if (fuwopt != NULL) {
+ fstrm_unix_writer_options_set_socket_path(fuwopt,
+ env->path);
+ fw = fstrm_unix_writer_init(fuwopt, fwopt);
+ }
+ } else
+ CHECK(ISC_R_FAILURE);
+
+ if (fw == NULL)
+ CHECK(ISC_R_FAILURE);
+
+ env->iothr = fstrm_iothr_init(*foptp, &fw);
+ if (env->iothr == NULL) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSTAP,
+ DNS_LOGMODULE_DNSTAP, ISC_LOG_WARNING,
+ "unable to initialize dnstap I/O thread");
+ fstrm_writer_destroy(&fw);
+ CHECK(ISC_R_FAILURE);
+ }
+ env->mode = mode;
+ env->fopt = *foptp;
+ *foptp = NULL;
+
+ isc_mem_attach(mctx, &env->mctx);
+
+ env->magic = DTENV_MAGIC;
+ *envp = env;
+
+ cleanup:
+ if (ffwopt != NULL)
+ fstrm_file_options_destroy(&ffwopt);
+
+ if (fuwopt != NULL)
+ fstrm_unix_writer_options_destroy(&fuwopt);
+
+ if (fwopt != NULL)
+ fstrm_writer_options_destroy(&fwopt);
+
+ if (result != ISC_R_SUCCESS) {
+ if (env != NULL) {
+ if (env->mctx != NULL)
+ isc_mem_detach(&env->mctx);
+ if (env->path != NULL)
+ isc_mem_free(mctx, env->path);
+ if (env->stats != NULL)
+ isc_stats_detach(&env->stats);
+ isc_mem_put(mctx, env, sizeof(dns_dtenv_t));
+ }
+ }
+
+ return (result);
+}
+
+isc_result_t
+dns_dt_reopen(dns_dtenv_t *env, int roll) {
+ isc_result_t result = ISC_R_SUCCESS;
+ fstrm_res res;
+ isc_logfile_t file;
+ struct fstrm_unix_writer_options *fuwopt = NULL;
+ struct fstrm_file_options *ffwopt = NULL;
+ struct fstrm_writer_options *fwopt = NULL;
+ struct fstrm_writer *fw = NULL;
+
+ REQUIRE(VALID_DTENV(env));
+
+ /*
+ * Check that we can create a new fw object.
+ */
+ fwopt = fstrm_writer_options_init();
+ if (fwopt == NULL)
+ return (ISC_R_NOMEMORY);
+
+ res = fstrm_writer_options_add_content_type(fwopt,
+ DNSTAP_CONTENT_TYPE,
+ sizeof(DNSTAP_CONTENT_TYPE) - 1);
+ if (res != fstrm_res_success)
+ CHECK(ISC_R_FAILURE);
+
+ if (env->mode == dns_dtmode_file) {
+ ffwopt = fstrm_file_options_init();
+ if (ffwopt != NULL) {
+ fstrm_file_options_set_file_path(ffwopt, env->path);
+ fw = fstrm_file_writer_init(ffwopt, fwopt);
+ }
+ } else if (env->mode == dns_dtmode_unix) {
+ fuwopt = fstrm_unix_writer_options_init();
+ if (fuwopt != NULL) {
+ fstrm_unix_writer_options_set_socket_path(fuwopt,
+ env->path);
+ fw = fstrm_unix_writer_init(fuwopt, fwopt);
+ }
+ } else
+ CHECK(ISC_R_NOTIMPLEMENTED);
+
+ if (fw == NULL)
+ CHECK(ISC_R_FAILURE);
+
+ /*
+ * We are committed here.
+ */
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSTAP,
+ DNS_LOGMODULE_DNSTAP, ISC_LOG_INFO,
+ "%s dnstap destination '%s'",
+ (roll < 0) ? "reopening" : "rolling",
+ env->path);
+
+ generation++;
+
+ if (env->iothr != NULL)
+ fstrm_iothr_destroy(&env->iothr);
+
+ if (env->mode == dns_dtmode_file && roll >= 0) {
+ /*
+ * Create a temporary isc_logfile_t structure so we can
+ * take advantage of the logfile rolling facility.
+ */
+ char *filename = isc_mem_strdup(env->mctx, env->path);
+ file.name = filename;
+ file.stream = NULL;
+ file.versions = roll != 0 ? roll : ISC_LOG_ROLLINFINITE;
+ file.maximum_size = 0;
+ file.maximum_reached = false;
+ result = isc_logfile_roll(&file);
+ isc_mem_free(env->mctx, filename);
+ CHECK(result);
+ }
+
+ env->iothr = fstrm_iothr_init(env->fopt, &fw);
+ if (env->iothr == NULL) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSTAP,
+ DNS_LOGMODULE_DNSTAP, ISC_LOG_WARNING,
+ "unable to initialize dnstap I/O thread");
+ CHECK(ISC_R_FAILURE);
+ }
+
+ cleanup:
+ if (ffwopt != NULL)
+ fstrm_file_options_destroy(&ffwopt);
+
+ if (fw != NULL)
+ fstrm_writer_destroy(&fw);
+
+ if (fwopt != NULL)
+ fstrm_writer_options_destroy(&fwopt);
+
+ if (fuwopt != NULL)
+ fstrm_unix_writer_options_destroy(&fuwopt);
+
+ return (result);
+}
+
+static isc_result_t
+toregion(dns_dtenv_t *env, isc_region_t *r, const char *str) {
+ unsigned char *p = NULL;
+
+ REQUIRE(r != NULL);
+
+ if (str != NULL) {
+ p = (unsigned char *) isc_mem_strdup(env->mctx, str);
+ if (p == NULL)
+ return (ISC_R_NOMEMORY);
+ }
+
+ if (r->base != NULL) {
+ isc_mem_free(env->mctx, r->base);
+ r->length = 0;
+ }
+
+ if (p != NULL) {
+ r->base = p;
+ r->length = strlen((char *) p);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_dt_setidentity(dns_dtenv_t *env, const char *identity) {
+ REQUIRE(VALID_DTENV(env));
+
+ return (toregion(env, &env->identity, identity));
+}
+
+isc_result_t
+dns_dt_setversion(dns_dtenv_t *env, const char *version) {
+ REQUIRE(VALID_DTENV(env));
+
+ return (toregion(env, &env->version, version));
+}
+
+static struct fstrm_iothr_queue *
+dt_queue(dns_dtenv_t *env) {
+ isc_result_t result;
+ struct ioq {
+ unsigned int generation;
+ struct fstrm_iothr_queue *ioq;
+ } *ioq;
+
+ REQUIRE(VALID_DTENV(env));
+
+ if (env->iothr == NULL)
+ return (NULL);
+
+ result = dt_init();
+ if (result != ISC_R_SUCCESS)
+ return (NULL);
+
+ ioq = (struct ioq *)isc_thread_key_getspecific(dt_key);
+ if (ioq != NULL && ioq->generation != generation) {
+ result = isc_thread_key_setspecific(dt_key, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (NULL);
+ free(ioq);
+ ioq = NULL;
+ }
+ if (ioq == NULL) {
+ ioq = malloc(sizeof(*ioq));
+ if (ioq == NULL)
+ return (NULL);
+ ioq->generation = generation;
+ ioq->ioq = fstrm_iothr_get_input_queue(env->iothr);
+ if (ioq->ioq == NULL) {
+ free(ioq);
+ return (NULL);
+ }
+ result = isc_thread_key_setspecific(dt_key, ioq);
+ if (result != ISC_R_SUCCESS) {
+ free(ioq);
+ return (NULL);
+ }
+ }
+
+ return (ioq->ioq);
+}
+
+void
+dns_dt_attach(dns_dtenv_t *source, dns_dtenv_t **destp) {
+ REQUIRE(VALID_DTENV(source));
+ REQUIRE(destp != NULL && *destp == NULL);
+
+ isc_refcount_increment(&source->refcount, NULL);
+ *destp = source;
+}
+
+isc_result_t
+dns_dt_getstats(dns_dtenv_t *env, isc_stats_t **statsp) {
+ REQUIRE(VALID_DTENV(env));
+ REQUIRE(statsp != NULL && *statsp == NULL);
+
+ if (env->stats == NULL)
+ return (ISC_R_NOTFOUND);
+ isc_stats_attach(env->stats, statsp);
+ return (ISC_R_SUCCESS);
+}
+
+static void
+destroy(dns_dtenv_t *env) {
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSTAP,
+ DNS_LOGMODULE_DNSTAP, ISC_LOG_INFO,
+ "closing dnstap");
+ env->magic = 0;
+
+ generation++;
+
+ if (env->iothr != NULL)
+ fstrm_iothr_destroy(&env->iothr);
+ if (env->fopt != NULL)
+ fstrm_iothr_options_destroy(&env->fopt);
+
+ if (env->identity.base != NULL) {
+ isc_mem_free(env->mctx, env->identity.base);
+ env->identity.length = 0;
+ }
+ if (env->version.base != NULL) {
+ isc_mem_free(env->mctx, env->version.base);
+ env->version.length = 0;
+ }
+ if (env->path != NULL)
+ isc_mem_free(env->mctx, env->path);
+ if (env->stats != NULL)
+ isc_stats_detach(&env->stats);
+
+ isc_mem_putanddetach(&env->mctx, env, sizeof(*env));
+}
+
+void
+dns_dt_detach(dns_dtenv_t **envp) {
+ unsigned int refs;
+ dns_dtenv_t *env;
+
+ REQUIRE(envp != NULL && VALID_DTENV(*envp));
+
+ env = *envp;
+ *envp = NULL;
+
+ isc_refcount_decrement(&env->refcount, &refs);
+ if (refs == 0)
+ destroy(env);
+}
+
+static isc_result_t
+pack_dt(const Dnstap__Dnstap *d, void **buf, size_t *sz) {
+ ProtobufCBufferSimple sbuf;
+
+ REQUIRE(d != NULL);
+ REQUIRE(sz != NULL);
+
+ memset(&sbuf, 0, sizeof(sbuf));
+ sbuf.base.append = protobuf_c_buffer_simple_append;
+ sbuf.len = 0;
+ sbuf.alloced = DNSTAP_INITIAL_BUF_SIZE;
+
+ /* Need to use malloc() here because protobuf uses free() */
+ sbuf.data = malloc(sbuf.alloced);
+ if (sbuf.data == NULL)
+ return (ISC_R_NOMEMORY);
+ sbuf.must_free_data = 1;
+
+ *sz = dnstap__dnstap__pack_to_buffer(d, (ProtobufCBuffer *) &sbuf);
+ if (sbuf.data == NULL)
+ return (ISC_R_FAILURE);
+ *buf = sbuf.data;
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+send_dt(dns_dtenv_t *env, void *buf, size_t len) {
+ struct fstrm_iothr_queue *ioq;
+ fstrm_res res;
+
+ REQUIRE(env != NULL);
+
+ if (buf == NULL)
+ return;
+
+ ioq = dt_queue(env);
+ if (ioq == NULL) {
+ free(buf);
+ return;
+ }
+
+ res = fstrm_iothr_submit(env->iothr, ioq, buf, len,
+ fstrm_free_wrapper, NULL);
+ if (res != fstrm_res_success) {
+ if (env->stats != NULL)
+ isc_stats_increment(env->stats,
+ dns_dnstapcounter_drop);
+ free(buf);
+ } else {
+ if (env->stats != NULL)
+ isc_stats_increment(env->stats,
+ dns_dnstapcounter_success);
+ }
+}
+
+static void
+init_msg(dns_dtenv_t *env, dns_dtmsg_t *dm, Dnstap__Message__Type mtype) {
+ memset(dm, 0, sizeof(*dm));
+ dm->d.base.descriptor = &dnstap__dnstap__descriptor;
+ dm->m.base.descriptor = &dnstap__message__descriptor;
+ dm->d.type = DNSTAP__DNSTAP__TYPE__MESSAGE;
+ dm->d.message = &dm->m;
+ dm->m.type = mtype;
+
+ if (env->identity.length != 0) {
+ dm->d.identity.data = env->identity.base;
+ dm->d.identity.len = env->identity.length;
+ dm->d.has_identity = true;
+ }
+
+ if (env->version.length != 0) {
+ dm->d.version.data = env->version.base;
+ dm->d.version.len = env->version.length;
+ dm->d.has_version = true;
+ }
+}
+
+static Dnstap__Message__Type
+dnstap_type(dns_dtmsgtype_t msgtype) {
+ switch (msgtype) {
+ case DNS_DTTYPE_SQ:
+ return (DNSTAP__MESSAGE__TYPE__STUB_QUERY);
+ case DNS_DTTYPE_SR:
+ return (DNSTAP__MESSAGE__TYPE__STUB_RESPONSE);
+ case DNS_DTTYPE_CQ:
+ return (DNSTAP__MESSAGE__TYPE__CLIENT_QUERY);
+ case DNS_DTTYPE_CR:
+ return (DNSTAP__MESSAGE__TYPE__CLIENT_RESPONSE);
+ case DNS_DTTYPE_AQ:
+ return (DNSTAP__MESSAGE__TYPE__AUTH_QUERY);
+ case DNS_DTTYPE_AR:
+ return (DNSTAP__MESSAGE__TYPE__AUTH_RESPONSE);
+ case DNS_DTTYPE_RQ:
+ return (DNSTAP__MESSAGE__TYPE__RESOLVER_QUERY);
+ case DNS_DTTYPE_RR:
+ return (DNSTAP__MESSAGE__TYPE__RESOLVER_RESPONSE);
+ case DNS_DTTYPE_FQ:
+ return (DNSTAP__MESSAGE__TYPE__FORWARDER_QUERY);
+ case DNS_DTTYPE_FR:
+ return (DNSTAP__MESSAGE__TYPE__FORWARDER_RESPONSE);
+ case DNS_DTTYPE_TQ:
+ return (DNSTAP__MESSAGE__TYPE__TOOL_QUERY);
+ case DNS_DTTYPE_TR:
+ return (DNSTAP__MESSAGE__TYPE__TOOL_RESPONSE);
+ default:
+ INSIST(0);
+ }
+}
+
+static void
+cpbuf(isc_buffer_t *buf, ProtobufCBinaryData *p, protobuf_c_boolean *has) {
+ p->data = isc_buffer_base(buf);
+ p->len = isc_buffer_usedlength(buf);
+ *has = 1;
+}
+
+static void
+setaddr(dns_dtmsg_t *dm, isc_sockaddr_t *sa, bool tcp,
+ ProtobufCBinaryData *addr, protobuf_c_boolean *has_addr,
+ uint32_t *port, protobuf_c_boolean *has_port)
+{
+ int family = isc_sockaddr_pf(sa);
+
+ if (family != AF_INET6 && family != AF_INET)
+ return;
+
+ if (family == AF_INET6) {
+ dm->m.socket_family = DNSTAP__SOCKET_FAMILY__INET6;
+ addr->data = sa->type.sin6.sin6_addr.s6_addr;
+ addr->len = 16;
+ *port = ntohs(sa->type.sin6.sin6_port);
+ } else {
+ dm->m.socket_family = DNSTAP__SOCKET_FAMILY__INET;
+ addr->data = (uint8_t *) &sa->type.sin.sin_addr.s_addr;
+ addr->len = 4;
+ *port = ntohs(sa->type.sin.sin_port);
+ }
+
+ if (tcp)
+ dm->m.socket_protocol = DNSTAP__SOCKET_PROTOCOL__TCP;
+ else
+ dm->m.socket_protocol = DNSTAP__SOCKET_PROTOCOL__UDP;
+
+ dm->m.has_socket_protocol = 1;
+ dm->m.has_socket_family = 1;
+ *has_addr = 1;
+ *has_port = 1;
+}
+
+void
+dns_dt_send(dns_view_t *view, dns_dtmsgtype_t msgtype,
+ isc_sockaddr_t *qaddr, isc_sockaddr_t *raddr,
+ bool tcp, isc_region_t *zone, isc_time_t *qtime,
+ isc_time_t *rtime, isc_buffer_t *buf)
+{
+ isc_time_t now, *t;
+ dns_dtmsg_t dm;
+
+ REQUIRE(DNS_VIEW_VALID(view));
+
+ if ((msgtype & view->dttypes) == 0)
+ return;
+
+ if (view->dtenv == NULL)
+ return;
+
+ REQUIRE(VALID_DTENV(view->dtenv));
+
+ TIME_NOW(&now);
+ t = &now;
+
+ init_msg(view->dtenv, &dm, dnstap_type(msgtype));
+
+ /* Query/response times */
+ switch (msgtype) {
+ case DNS_DTTYPE_AR:
+ case DNS_DTTYPE_CR:
+ case DNS_DTTYPE_RR:
+ case DNS_DTTYPE_FR:
+ case DNS_DTTYPE_SR:
+ case DNS_DTTYPE_TR:
+ if (rtime != NULL)
+ t = rtime;
+
+ dm.m.response_time_sec = isc_time_seconds(t);
+ dm.m.has_response_time_sec = 1;
+ dm.m.response_time_nsec = isc_time_nanoseconds(t);
+ dm.m.has_response_time_nsec = 1;
+
+ cpbuf(buf, &dm.m.response_message, &dm.m.has_response_message);
+
+ /* Types RR and FR get both query and response times */
+ if (msgtype == DNS_DTTYPE_CR || msgtype == DNS_DTTYPE_AR)
+ break;
+
+ /* FALLTHROUGH */
+ case DNS_DTTYPE_AQ:
+ case DNS_DTTYPE_CQ:
+ case DNS_DTTYPE_FQ:
+ case DNS_DTTYPE_RQ:
+ case DNS_DTTYPE_SQ:
+ case DNS_DTTYPE_TQ:
+ if (qtime != NULL)
+ t = qtime;
+
+ dm.m.query_time_sec = isc_time_seconds(t);
+ dm.m.has_query_time_sec = 1;
+ dm.m.query_time_nsec = isc_time_nanoseconds(t);
+ dm.m.has_query_time_nsec = 1;
+
+ cpbuf(buf, &dm.m.query_message, &dm.m.has_query_message);
+ break;
+ default:
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSTAP,
+ DNS_LOGMODULE_DNSTAP, ISC_LOG_ERROR,
+ "invalid dnstap message type %d", msgtype);
+ return;
+ }
+
+ /* Zone/bailiwick */
+ switch (msgtype) {
+ case DNS_DTTYPE_AR:
+ case DNS_DTTYPE_RQ:
+ case DNS_DTTYPE_RR:
+ case DNS_DTTYPE_FQ:
+ case DNS_DTTYPE_FR:
+ if (zone != NULL && zone->base != NULL && zone->length != 0) {
+ dm.m.query_zone.data = zone->base;
+ dm.m.query_zone.len = zone->length;
+ dm.m.has_query_zone = 1;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (qaddr != NULL) {
+ setaddr(&dm, qaddr, tcp,
+ &dm.m.query_address, &dm.m.has_query_address,
+ &dm.m.query_port, &dm.m.has_query_port);
+ }
+ if (raddr != NULL) {
+ setaddr(&dm, raddr, tcp,
+ &dm.m.response_address, &dm.m.has_response_address,
+ &dm.m.response_port, &dm.m.has_response_port);
+ }
+
+ if (pack_dt(&dm.d, &dm.buf, &dm.len) == ISC_R_SUCCESS)
+ send_dt(view->dtenv, dm.buf, dm.len);
+}
+
+void
+dns_dt_shutdown() {
+ if (dt_mctx != NULL)
+ isc_mem_detach(&dt_mctx);
+}
+
+static isc_result_t
+putstr(isc_buffer_t **b, const char *str) {
+ isc_result_t result;
+
+ result = isc_buffer_reserve(b, strlen(str));
+ if (result != ISC_R_SUCCESS)
+ return (ISC_R_NOSPACE);
+
+ isc_buffer_putstr(*b, str);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+putaddr(isc_buffer_t **b, isc_region_t *ip) {
+ char buf[64];
+
+ if (ip->length == 4) {
+ if (!inet_ntop(AF_INET, ip->base, buf, sizeof(buf)))
+ return (ISC_R_FAILURE);
+ } else if (ip->length == 16) {
+ if (!inet_ntop(AF_INET6, ip->base, buf, sizeof(buf)))
+ return (ISC_R_FAILURE);
+ } else
+ return (ISC_R_BADADDRESSFORM);
+
+ return (putstr(b, buf));
+}
+
+static bool
+dnstap_file(struct fstrm_reader *r) {
+ fstrm_res res;
+ const struct fstrm_control *control = NULL;
+ const uint8_t *rtype = NULL;
+ size_t dlen = strlen(DNSTAP_CONTENT_TYPE), rlen = 0;
+ size_t n = 0;
+
+ res = fstrm_reader_get_control(r, FSTRM_CONTROL_START, &control);
+ if (res != fstrm_res_success)
+ return (false);
+
+ res = fstrm_control_get_num_field_content_type(control, &n);
+ if (res != fstrm_res_success)
+ return (false);
+ if (n > 0) {
+ res = fstrm_control_get_field_content_type(control, 0,
+ &rtype, &rlen);
+ if (res != fstrm_res_success)
+ return (false);
+
+ if (rlen != dlen)
+ return (false);
+
+ if (memcmp(DNSTAP_CONTENT_TYPE, rtype, dlen) == 0)
+ return (true);
+ }
+
+ return (false);
+}
+
+isc_result_t
+dns_dt_open(const char *filename, dns_dtmode_t mode, isc_mem_t *mctx,
+ dns_dthandle_t **handlep)
+{
+ isc_result_t result;
+ struct fstrm_file_options *fopt = NULL;
+ fstrm_res res;
+ dns_dthandle_t *handle;
+
+ REQUIRE(handlep != NULL && *handlep == NULL);
+
+ handle = isc_mem_get(mctx, sizeof(*handle));
+ if (handle == NULL)
+ CHECK(ISC_R_NOMEMORY);
+
+ handle->mode = mode;
+ handle->mctx = NULL;
+
+ switch(mode) {
+ case dns_dtmode_file:
+ fopt = fstrm_file_options_init();
+ if (fopt == NULL)
+ CHECK(ISC_R_NOMEMORY);
+
+ fstrm_file_options_set_file_path(fopt, filename);
+
+ handle->reader = fstrm_file_reader_init(fopt, NULL);
+ if (handle->reader == NULL)
+ CHECK(ISC_R_NOMEMORY);
+
+ res = fstrm_reader_open(handle->reader);
+ if (res != fstrm_res_success)
+ CHECK(ISC_R_FAILURE);
+
+ if (!dnstap_file(handle->reader))
+ CHECK(DNS_R_BADDNSTAP);
+ break;
+ case dns_dtmode_unix:
+ return (ISC_R_NOTIMPLEMENTED);
+ default:
+ INSIST(0);
+ }
+
+ isc_mem_attach(mctx, &handle->mctx);
+ result = ISC_R_SUCCESS;
+ *handlep = handle;
+ handle = NULL;
+
+ cleanup:
+ if (result != ISC_R_SUCCESS && handle->reader != NULL) {
+ fstrm_reader_destroy(&handle->reader);
+ handle->reader = NULL;
+ }
+ if (fopt != NULL)
+ fstrm_file_options_destroy(&fopt);
+ if (handle != NULL)
+ isc_mem_put(mctx, handle, sizeof(*handle));
+ return (result);
+}
+
+isc_result_t
+dns_dt_getframe(dns_dthandle_t *handle, uint8_t **bufp, size_t *sizep) {
+ const uint8_t *data;
+ fstrm_res res;
+
+ REQUIRE(handle != NULL);
+ REQUIRE(bufp != NULL);
+ REQUIRE(sizep != NULL);
+
+ data = (const uint8_t *) *bufp;
+
+ res = fstrm_reader_read(handle->reader, &data, sizep);
+ switch (res) {
+ case fstrm_res_success:
+ if (data == NULL)
+ return (ISC_R_FAILURE);
+ DE_CONST(data, *bufp);
+ return (ISC_R_SUCCESS);
+ case fstrm_res_stop:
+ return (ISC_R_NOMORE);
+ default:
+ return (ISC_R_FAILURE);
+ }
+}
+
+void
+dns_dt_close(dns_dthandle_t **handlep) {
+ dns_dthandle_t *handle;
+
+ REQUIRE(handlep != NULL && *handlep != NULL);
+
+ handle = *handlep;
+ *handlep = NULL;
+
+ if (handle->reader != NULL) {
+ fstrm_reader_destroy(&handle->reader);
+ handle->reader = NULL;
+ }
+ isc_mem_putanddetach(&handle->mctx, handle, sizeof(*handle));
+}
+
+isc_result_t
+dns_dt_parse(isc_mem_t *mctx, isc_region_t *src, dns_dtdata_t **destp) {
+ isc_result_t result;
+ Dnstap__Message *m;
+ dns_dtdata_t *d = NULL;
+ isc_buffer_t b;
+
+ REQUIRE(src != NULL);
+ REQUIRE(destp != NULL && *destp == NULL);
+
+ d = isc_mem_get(mctx, sizeof(*d));
+ if (d == NULL)
+ return (ISC_R_NOMEMORY);
+
+ memset(d, 0, sizeof(*d));
+ isc_mem_attach(mctx, &d->mctx);
+
+ d->frame = dnstap__dnstap__unpack(NULL, src->length, src->base);
+ if (d->frame == NULL)
+ CHECK(ISC_R_NOMEMORY);
+
+ if (d->frame->type != DNSTAP__DNSTAP__TYPE__MESSAGE)
+ CHECK(DNS_R_BADDNSTAP);
+
+ m = d->frame->message;
+
+ /* Message type */
+ switch (m->type) {
+ case DNSTAP__MESSAGE__TYPE__AUTH_QUERY:
+ d->type = DNS_DTTYPE_AQ;
+ break;
+ case DNSTAP__MESSAGE__TYPE__AUTH_RESPONSE:
+ d->type = DNS_DTTYPE_AR;
+ break;
+ case DNSTAP__MESSAGE__TYPE__CLIENT_QUERY:
+ d->type = DNS_DTTYPE_CQ;
+ break;
+ case DNSTAP__MESSAGE__TYPE__CLIENT_RESPONSE:
+ d->type = DNS_DTTYPE_CR;
+ break;
+ case DNSTAP__MESSAGE__TYPE__FORWARDER_QUERY:
+ d->type = DNS_DTTYPE_FQ;
+ break;
+ case DNSTAP__MESSAGE__TYPE__FORWARDER_RESPONSE:
+ d->type = DNS_DTTYPE_FR;
+ break;
+ case DNSTAP__MESSAGE__TYPE__RESOLVER_QUERY:
+ d->type = DNS_DTTYPE_RQ;
+ break;
+ case DNSTAP__MESSAGE__TYPE__RESOLVER_RESPONSE:
+ d->type = DNS_DTTYPE_RR;
+ break;
+ case DNSTAP__MESSAGE__TYPE__STUB_QUERY:
+ d->type = DNS_DTTYPE_SQ;
+ break;
+ case DNSTAP__MESSAGE__TYPE__STUB_RESPONSE:
+ d->type = DNS_DTTYPE_SR;
+ break;
+ case DNSTAP__MESSAGE__TYPE__TOOL_QUERY:
+ d->type = DNS_DTTYPE_TQ;
+ break;
+ case DNSTAP__MESSAGE__TYPE__TOOL_RESPONSE:
+ d->type = DNS_DTTYPE_TR;
+ break;
+ default:
+ CHECK(DNS_R_BADDNSTAP);
+ }
+
+ /* Query? */
+ if ((d->type & DNS_DTTYPE_QUERY) != 0)
+ d->query = true;
+ else
+ d->query = false;
+
+ /* Parse DNS message */
+ if (d->query && m->has_query_message) {
+ d->msgdata.base = m->query_message.data;
+ d->msgdata.length = m->query_message.len;
+ } else if (!d->query && m->has_response_message) {
+ d->msgdata.base = m->response_message.data;
+ d->msgdata.length = m->response_message.len;
+ }
+
+ isc_buffer_init(&b, d->msgdata.base, d->msgdata.length);
+ isc_buffer_add(&b, d->msgdata.length);
+ CHECK(dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &d->msg));
+ result = dns_message_parse(d->msg, &b, 0);
+ if (result != ISC_R_SUCCESS) {
+ if (result != DNS_R_RECOVERABLE)
+ dns_message_destroy(&d->msg);
+ result = ISC_R_SUCCESS;
+ }
+
+ /* Timestamp */
+ if (d->query) {
+ if (m->has_query_time_sec && m->has_query_time_nsec)
+ isc_time_set(&d->qtime, m->query_time_sec,
+ m->query_time_nsec);
+ } else {
+ if (m->has_response_time_sec && m->has_response_time_nsec)
+ isc_time_set(&d->rtime, m->response_time_sec,
+ m->response_time_nsec);
+ }
+
+ /* Peer address */
+ if (m->has_query_address) {
+ d->qaddr.base = m->query_address.data;
+ d->qaddr.length = m->query_address.len;
+ }
+ if (m->has_query_port) {
+ d->qport = m->query_port;
+ }
+
+ if (m->has_response_address) {
+ d->raddr.base = m->response_address.data;
+ d->raddr.length = m->response_address.len;
+ }
+ if (m->has_response_port) {
+ d->rport = m->response_port;
+ }
+
+ /* Socket protocol */
+ if (m->has_socket_protocol) {
+ const ProtobufCEnumValue *type =
+ protobuf_c_enum_descriptor_get_value(
+ &dnstap__socket_protocol__descriptor,
+ m->socket_protocol);
+ if (type != NULL &&
+ type->value == DNSTAP__SOCKET_PROTOCOL__TCP)
+ d->tcp = true;
+ else
+ d->tcp = false;
+ }
+
+ /* Query tuple */
+ if (d->msg != NULL) {
+ dns_name_t *name = NULL;
+ dns_rdataset_t *rdataset;
+
+ CHECK(dns_message_firstname(d->msg, DNS_SECTION_QUESTION));
+ dns_message_currentname(d->msg, DNS_SECTION_QUESTION, &name);
+ rdataset = ISC_LIST_HEAD(name->list);
+
+ dns_name_format(name, d->namebuf, sizeof(d->namebuf));
+ dns_rdatatype_format(rdataset->type, d->typebuf,
+ sizeof(d->typebuf));
+ dns_rdataclass_format(rdataset->rdclass, d->classbuf,
+ sizeof(d->classbuf));
+ }
+
+ *destp = d;
+
+ cleanup:
+ if (result != ISC_R_SUCCESS)
+ dns_dtdata_free(&d);
+
+ return (result);
+}
+
+isc_result_t
+dns_dt_datatotext(dns_dtdata_t *d, isc_buffer_t **dest) {
+ isc_result_t result;
+ char buf[100];
+
+ REQUIRE(d != NULL);
+ REQUIRE(dest != NULL && *dest != NULL);
+
+ memset(buf, 0, sizeof(buf));
+
+ /* Timestamp */
+ if (d->query && !isc_time_isepoch(&d->qtime))
+ isc_time_formattimestamp(&d->qtime, buf, sizeof(buf));
+ else if (!d->query && !isc_time_isepoch(&d->rtime))
+ isc_time_formattimestamp(&d->rtime, buf, sizeof(buf));
+
+ if (buf[0] == '\0')
+ CHECK(putstr(dest, "???\?-?\?-?? ??:??:??.??? "));
+ else {
+ CHECK(putstr(dest, buf));
+ CHECK(putstr(dest, " "));
+ }
+
+ /* Type mnemonic */
+ switch (d->type) {
+ case DNS_DTTYPE_AQ:
+ CHECK(putstr(dest, "AQ "));
+ break;
+ case DNS_DTTYPE_AR:
+ CHECK(putstr(dest, "AR "));
+ break;
+ case DNS_DTTYPE_CQ:
+ CHECK(putstr(dest, "CQ "));
+ break;
+ case DNS_DTTYPE_CR:
+ CHECK(putstr(dest, "CR "));
+ break;
+ case DNS_DTTYPE_FQ:
+ CHECK(putstr(dest, "FQ "));
+ break;
+ case DNS_DTTYPE_FR:
+ CHECK(putstr(dest, "FR "));
+ break;
+ case DNS_DTTYPE_RQ:
+ CHECK(putstr(dest, "RQ "));
+ break;
+ case DNS_DTTYPE_RR:
+ CHECK(putstr(dest, "RR "));
+ break;
+ case DNS_DTTYPE_SQ:
+ CHECK(putstr(dest, "SQ "));
+ break;
+ case DNS_DTTYPE_SR:
+ CHECK(putstr(dest, "SR "));
+ break;
+ case DNS_DTTYPE_TQ:
+ CHECK(putstr(dest, "TQ "));
+ break;
+ case DNS_DTTYPE_TR:
+ CHECK(putstr(dest, "TR "));
+ break;
+ default:
+ return (DNS_R_BADDNSTAP);
+ }
+
+ /* Query and response addresses */
+ if (d->qaddr.length != 0) {
+ CHECK(putaddr(dest, &d->qaddr));
+ snprintf(buf, sizeof(buf), ":%u", d->qport);
+ CHECK(putstr(dest, buf));
+ } else {
+ CHECK(putstr(dest, "?"));
+ }
+ if ((d->type & DNS_DTTYPE_QUERY) != 0) {
+ CHECK(putstr(dest, " -> "));
+ } else {
+ CHECK(putstr(dest, " <- "));
+ }
+ if (d->raddr.length != 0) {
+ CHECK(putaddr(dest, &d->raddr));
+ snprintf(buf, sizeof(buf), ":%u", d->rport);
+ CHECK(putstr(dest, buf));
+ } else {
+ CHECK(putstr(dest, "?"));
+ }
+
+ CHECK(putstr(dest, " "));
+
+ /* Protocol */
+ if (d->tcp)
+ CHECK(putstr(dest, "TCP "));
+ else
+ CHECK(putstr(dest, "UDP "));
+
+ /* Message size */
+ if (d->msgdata.base != NULL) {
+ snprintf(buf, sizeof(buf), "%zub ", (size_t) d->msgdata.length);
+ CHECK(putstr(dest, buf));
+ } else
+ CHECK(putstr(dest, "0b "));
+
+ /* Query tuple */
+ if (d->namebuf[0] == '\0')
+ CHECK(putstr(dest, "?/"));
+ else {
+ CHECK(putstr(dest, d->namebuf));
+ CHECK(putstr(dest, "/"));
+ }
+
+ if (d->classbuf[0] == '\0')
+ CHECK(putstr(dest, "?/"));
+ else {
+ CHECK(putstr(dest, d->classbuf));
+ CHECK(putstr(dest, "/"));
+ }
+
+ if (d->typebuf[0] == '\0')
+ CHECK(putstr(dest, "?"));
+ else
+ CHECK(putstr(dest, d->typebuf));
+
+ CHECK(isc_buffer_reserve(dest, 1));
+ isc_buffer_putuint8(*dest, 0);
+
+ cleanup:
+ return (result);
+}
+
+void
+dns_dtdata_free(dns_dtdata_t **dp) {
+ dns_dtdata_t *d;
+
+ REQUIRE(dp != NULL && *dp != NULL);
+
+ d = *dp;
+
+ if (d->msg != NULL)
+ dns_message_destroy(&d->msg);
+ if (d->frame != NULL)
+ dnstap__dnstap__free_unpacked(d->frame, NULL);
+
+ isc_mem_putanddetach(&d->mctx, d, sizeof(*d));
+
+ *dp = NULL;
+}
diff --git a/lib/dns/dnstap.proto b/lib/dns/dnstap.proto
new file mode 100644
index 0000000..1ed1bb0
--- /dev/null
+++ b/lib/dns/dnstap.proto
@@ -0,0 +1,268 @@
+// dnstap: flexible, structured event replication format for DNS software
+//
+// This file contains the protobuf schemas for the "dnstap" structured event
+// replication format for DNS software.
+
+// Written in 2013-2014 by Farsight Security, Inc.
+//
+// To the extent possible under law, the author(s) have dedicated all
+// copyright and related and neighboring rights to this file to the public
+// domain worldwide. This file is distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along
+// with this file. If not, see:
+//
+// <http://creativecommons.org/publicdomain/zero/1.0/>.
+
+package dnstap;
+
+// "Dnstap": this is the top-level dnstap type, which is a "union" type that
+// contains other kinds of dnstap payloads, although currently only one type
+// of dnstap payload is defined.
+// See: https://developers.google.com/protocol-buffers/docs/techniques#union
+message Dnstap {
+ // DNS server identity.
+ // If enabled, this is the identity string of the DNS server which generated
+ // this message. Typically this would be the same string as returned by an
+ // "NSID" (RFC 5001) query.
+ optional bytes identity = 1;
+
+ // DNS server version.
+ // If enabled, this is the version string of the DNS server which generated
+ // this message. Typically this would be the same string as returned by a
+ // "version.bind" query.
+ optional bytes version = 2;
+
+ // Extra data for this payload.
+ // This field can be used for adding an arbitrary byte-string annotation to
+ // the payload. No encoding or interpretation is applied or enforced.
+ optional bytes extra = 3;
+
+ // Identifies which field below is filled in.
+ enum Type {
+ MESSAGE = 1;
+ }
+ required Type type = 15;
+
+ // One of the following will be filled in.
+ optional Message message = 14;
+}
+
+// SocketFamily: the network protocol family of a socket. This specifies how
+// to interpret "network address" fields.
+enum SocketFamily {
+ INET = 1; // IPv4 (RFC 791)
+ INET6 = 2; // IPv6 (RFC 2460)
+}
+
+// SocketProtocol: the transport protocol of a socket. This specifies how to
+// interpret "transport port" fields.
+enum SocketProtocol {
+ UDP = 1; // User Datagram Protocol (RFC 768)
+ TCP = 2; // Transmission Control Protocol (RFC 793)
+}
+
+// Message: a wire-format (RFC 1035 section 4) DNS message and associated
+// metadata. Applications generating "Message" payloads should follow
+// certain requirements based on the MessageType, see below.
+message Message {
+
+ // There are eight types of "Message" defined that correspond to the
+ // four arrows in the following diagram, slightly modified from RFC 1035
+ // section 2:
+
+ // +---------+ +----------+ +--------+
+ // | | query | | query | |
+ // | Stub |-SQ--------CQ->| Recursive|-RQ----AQ->| Auth. |
+ // | Resolver| | Server | | Name |
+ // | |<-SR--------CR-| |<-RR----AR-| Server |
+ // +---------+ response | | response | |
+ // +----------+ +--------+
+
+ // Each arrow has two Type values each, one for each "end" of each arrow,
+ // because these are considered to be distinct events. Each end of each
+ // arrow on the diagram above has been marked with a two-letter Type
+ // mnemonic. Clockwise from upper left, these mnemonic values are:
+ //
+ // SQ: STUB_QUERY
+ // CQ: CLIENT_QUERY
+ // RQ: RESOLVER_QUERY
+ // AQ: AUTH_QUERY
+ // AR: AUTH_RESPONSE
+ // RR: RESOLVER_RESPONSE
+ // CR: CLIENT_RESPONSE
+ // SR: STUB_RESPONSE
+
+ // Two additional types of "Message" have been defined for the
+ // "forwarding" case where an upstream DNS server is responsible for
+ // further recursion. These are not shown on the diagram above, but have
+ // the following mnemonic values:
+
+ // FQ: FORWARDER_QUERY
+ // FR: FORWARDER_RESPONSE
+
+ // The "Message" Type values are defined below.
+
+ enum Type {
+ // AUTH_QUERY is a DNS query message received from a resolver by an
+ // authoritative name server, from the perspective of the authorative
+ // name server.
+ AUTH_QUERY = 1;
+
+ // AUTH_RESPONSE is a DNS response message sent from an authoritative
+ // name server to a resolver, from the perspective of the authoritative
+ // name server.
+ AUTH_RESPONSE = 2;
+
+ // RESOLVER_QUERY is a DNS query message sent from a resolver to an
+ // authoritative name server, from the perspective of the resolver.
+ // Resolvers typically clear the RD (recursion desired) bit when
+ // sending queries.
+ RESOLVER_QUERY = 3;
+
+ // RESOLVER_RESPONSE is a DNS response message received from an
+ // authoritative name server by a resolver, from the perspective of
+ // the resolver.
+ RESOLVER_RESPONSE = 4;
+
+ // CLIENT_QUERY is a DNS query message sent from a client to a DNS
+ // server which is expected to perform further recursion, from the
+ // perspective of the DNS server. The client may be a stub resolver or
+ // forwarder or some other type of software which typically sets the RD
+ // (recursion desired) bit when querying the DNS server. The DNS server
+ // may be a simple forwarding proxy or it may be a full recursive
+ // resolver.
+ CLIENT_QUERY = 5;
+
+ // CLIENT_RESPONSE is a DNS response message sent from a DNS server to
+ // a client, from the perspective of the DNS server. The DNS server
+ // typically sets the RA (recursion available) bit when responding.
+ CLIENT_RESPONSE = 6;
+
+ // FORWARDER_QUERY is a DNS query message sent from a downstream DNS
+ // server to an upstream DNS server which is expected to perform
+ // further recursion, from the perspective of the downstream DNS
+ // server.
+ FORWARDER_QUERY = 7;
+
+ // FORWARDER_RESPONSE is a DNS response message sent from an upstream
+ // DNS server performing recursion to a downstream DNS server, from the
+ // perspective of the downstream DNS server.
+ FORWARDER_RESPONSE = 8;
+
+ // STUB_QUERY is a DNS query message sent from a stub resolver to a DNS
+ // server, from the perspective of the stub resolver.
+ STUB_QUERY = 9;
+
+ // STUB_RESPONSE is a DNS response message sent from a DNS server to a
+ // stub resolver, from the perspective of the stub resolver.
+ STUB_RESPONSE = 10;
+
+ // TOOL_QUERY is a DNS query message sent from a DNS software tool to a
+ // DNS server, from the perspective of the tool.
+ TOOL_QUERY = 11;
+
+ // TOOL_RESPONSE is a DNS response message received by a DNS software
+ // tool from a DNS server, from the perspective of the tool.
+ TOOL_RESPONSE = 12;
+ }
+
+ // One of the Type values described above.
+ required Type type = 1;
+
+ // One of the SocketFamily values described above.
+ optional SocketFamily socket_family = 2;
+
+ // One of the SocketProtocol values described above.
+ optional SocketProtocol socket_protocol = 3;
+
+ // The network address of the message initiator.
+ // For SocketFamily INET, this field is 4 octets (IPv4 address).
+ // For SocketFamily INET6, this field is 16 octets (IPv6 address).
+ optional bytes query_address = 4;
+
+ // The network address of the message responder.
+ // For SocketFamily INET, this field is 4 octets (IPv4 address).
+ // For SocketFamily INET6, this field is 16 octets (IPv6 address).
+ optional bytes response_address = 5;
+
+ // The transport port of the message initiator.
+ // This is a 16-bit UDP or TCP port number, depending on SocketProtocol.
+ optional uint32 query_port = 6;
+
+ // The transport port of the message responder.
+ // This is a 16-bit UDP or TCP port number, depending on SocketProtocol.
+ optional uint32 response_port = 7;
+
+ // The time at which the DNS query message was sent or received, depending
+ // on whether this is an AUTH_QUERY, RESOLVER_QUERY, or CLIENT_QUERY.
+ // This is the number of seconds since the UNIX epoch.
+ optional uint64 query_time_sec = 8;
+
+ // The time at which the DNS query message was sent or received.
+ // This is the seconds fraction, expressed as a count of nanoseconds.
+ optional fixed32 query_time_nsec = 9;
+
+ // The initiator's original wire-format DNS query message, verbatim.
+ optional bytes query_message = 10;
+
+ // The "zone" or "bailiwick" pertaining to the DNS query message.
+ // This is a wire-format DNS domain name.
+ optional bytes query_zone = 11;
+
+ // The time at which the DNS response message was sent or received,
+ // depending on whether this is an AUTH_RESPONSE, RESOLVER_RESPONSE, or
+ // CLIENT_RESPONSE.
+ // This is the number of seconds since the UNIX epoch.
+ optional uint64 response_time_sec = 12;
+
+ // The time at which the DNS response message was sent or received.
+ // This is the seconds fraction, expressed as a count of nanoseconds.
+ optional fixed32 response_time_nsec = 13;
+
+ // The responder's original wire-format DNS response message, verbatim.
+ optional bytes response_message = 14;
+}
+
+// All fields except for 'type' in the Message schema are optional.
+// It is recommended that at least the following fields be filled in for
+// particular types of Messages.
+
+// AUTH_QUERY:
+// socket_family, socket_protocol
+// query_address, query_port
+// query_message
+// query_time_sec, query_time_nsec
+
+// AUTH_RESPONSE:
+// socket_family, socket_protocol
+// query_address, query_port
+// query_time_sec, query_time_nsec
+// response_message
+// response_time_sec, response_time_nsec
+
+// RESOLVER_QUERY:
+// socket_family, socket_protocol
+// query_message
+// query_time_sec, query_time_nsec
+// query_zone
+// response_address, response_port
+
+// RESOLVER_RESPONSE:
+// socket_family, socket_protocol
+// query_time_sec, query_time_nsec
+// query_zone
+// response_address, response_port
+// response_message
+// response_time_sec, response_time_nsec
+
+// CLIENT_QUERY:
+// socket_family, socket_protocol
+// query_message
+// query_time_sec, query_time_nsec
+
+// CLIENT_RESPONSE:
+// socket_family, socket_protocol
+// query_time_sec, query_time_nsec
+// response_message
+// response_time_sec, response_time_nsec
diff --git a/lib/dns/ds.c b/lib/dns/ds.c
new file mode 100644
index 0000000..d1a507b
--- /dev/null
+++ b/lib/dns/ds.c
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \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>
+
+#if defined(HAVE_OPENSSL_GOST) || defined(HAVE_PKCS11_GOST)
+#include "dst_gost.h"
+#endif
+
+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_SHA384_DIGESTLENGTH];
+ isc_region_t r;
+ isc_buffer_t b;
+ dns_rdata_ds_t ds;
+ isc_sha1_t sha1;
+ isc_sha256_t sha256;
+ isc_sha384_t sha384;
+#if defined(HAVE_OPENSSL_GOST) || defined(HAVE_PKCS11_GOST)
+ isc_gost_t gost;
+#endif
+
+ REQUIRE(key != NULL);
+ REQUIRE(key->type == dns_rdatatype_dnskey);
+
+ if (!dst_ds_digest_supported(digest_type))
+ return (ISC_R_NOTIMPLEMENTED);
+
+ name = dns_fixedname_initname(&fname);
+ (void)dns_name_downcase(owner, name, NULL);
+
+ memset(buffer, 0, DNS_DS_BUFFERSIZE);
+ isc_buffer_init(&b, buffer, DNS_DS_BUFFERSIZE);
+
+ switch (digest_type) {
+ case DNS_DSDIGEST_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);
+ break;
+
+#if defined(HAVE_OPENSSL_GOST) || defined(HAVE_PKCS11_GOST)
+#define RETERR(x) do { \
+ isc_result_t ret = (x); \
+ if (ret != ISC_R_SUCCESS) { \
+ isc_gost_invalidate(&gost); \
+ return (ret); \
+ } \
+} while (0)
+
+ case DNS_DSDIGEST_GOST:
+ RETERR(isc_gost_init(&gost));
+ dns_name_toregion(name, &r);
+ RETERR(isc_gost_update(&gost, r.base, r.length));
+ dns_rdata_toregion(key, &r);
+ INSIST(r.length >= 4);
+ RETERR(isc_gost_update(&gost, r.base, r.length));
+ RETERR(isc_gost_final(&gost, digest));
+ break;
+#endif
+
+ case DNS_DSDIGEST_SHA384:
+ isc_sha384_init(&sha384);
+ dns_name_toregion(name, &r);
+ isc_sha384_update(&sha384, r.base, r.length);
+ dns_rdata_toregion(key, &r);
+ INSIST(r.length >= 4);
+ isc_sha384_update(&sha384, r.base, r.length);
+ isc_sha384_final(digest, &sha384);
+ break;
+
+ case DNS_DSDIGEST_SHA256:
+ default:
+ 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);
+ break;
+ }
+
+ 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;
+ switch (digest_type) {
+ case DNS_DSDIGEST_SHA1:
+ ds.length = ISC_SHA1_DIGESTLENGTH;
+ break;
+
+#if defined(HAVE_OPENSSL_GOST) || defined(HAVE_PKCS11_GOST)
+ case DNS_DSDIGEST_GOST:
+ ds.length = ISC_GOST_DIGESTLENGTH;
+ break;
+#endif
+
+ case DNS_DSDIGEST_SHA384:
+ ds.length = ISC_SHA384_DIGESTLENGTH;
+ break;
+
+ case DNS_DSDIGEST_SHA256:
+ default:
+ ds.length = ISC_SHA256_DIGESTLENGTH;
+ break;
+ }
+ ds.digest = digest;
+
+ return (dns_rdata_fromstruct(rdata, key->rdclass, dns_rdatatype_ds,
+ &ds, &b));
+}
diff --git a/lib/dns/dst_api.c b/lib/dns/dst_api.c
new file mode 100644
index 0000000..97fee68
--- /dev/null
+++ b/lib/dns/dst_api.c
@@ -0,0 +1,2029 @@
+/*
+ * Portions Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Portions Copyright (C) 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.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <time.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/platform.h>
+#include <isc/print.h>
+#include <isc/refcount.h>
+#include <isc/random.h>
+#include <isc/safe.h>
+#include <isc/string.h>
+#include <isc/time.h>
+#include <isc/util.h>
+#include <isc/file.h>
+
+#include <pk11/site.h>
+
+#define DST_KEY_INTERNAL
+
+#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 bool dst_initialized = false;
+
+void gss_log(int level, const char *fmt, ...) ISC_FORMAT_PRINTF(2, 3);
+
+LIBDNS_EXTERNAL_DATA 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,
+ dns_ttl_t ttl,
+ 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, int len,
+ const char *dirname, 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); \
+
+#if defined(OPENSSL)
+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);
+}
+#endif
+
+isc_result_t
+dst_lib_init(isc_mem_t *mctx, isc_entropy_t *ectx, unsigned int eflags) {
+ return (dst_lib_init2(mctx, ectx, NULL, eflags));
+}
+
+isc_result_t
+dst_lib_init2(isc_mem_t *mctx, isc_entropy_t *ectx,
+ const char *engine, unsigned int eflags) {
+ isc_result_t result;
+
+ REQUIRE(mctx != NULL);
+ REQUIRE(dst_initialized == false);
+
+#if !defined(OPENSSL) && !defined(PKCS11CRYPTO)
+ UNUSED(engine);
+#endif
+
+ dst__memory_pool = NULL;
+
+#if defined(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);
+#ifndef OPENSSL_LEAKS
+ isc_mem_setdestroycheck(dst__memory_pool, false);
+#endif
+#else /* OPENSSL */
+ isc_mem_attach(mctx, &dst__memory_pool);
+#endif /* OPENSSL */
+ if (ectx != NULL) {
+ isc_entropy_attach(ectx, &dst_entropy_pool);
+ dst_entropy_flags = eflags;
+ }
+
+ dst_result_register();
+
+ memset(dst_t_func, 0, sizeof(dst_t_func));
+#ifndef PK11_MD5_DISABLE
+ RETERR(dst__hmacmd5_init(&dst_t_func[DST_ALG_HMACMD5]));
+#endif
+ 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(engine));
+#ifndef PK11_MD5_DISABLE
+ RETERR(dst__opensslrsa_init(&dst_t_func[DST_ALG_RSAMD5],
+ DST_ALG_RSAMD5));
+#endif
+ RETERR(dst__opensslrsa_init(&dst_t_func[DST_ALG_RSASHA1],
+ DST_ALG_RSASHA1));
+ RETERR(dst__opensslrsa_init(&dst_t_func[DST_ALG_NSEC3RSASHA1],
+ DST_ALG_NSEC3RSASHA1));
+ RETERR(dst__opensslrsa_init(&dst_t_func[DST_ALG_RSASHA256],
+ DST_ALG_RSASHA256));
+ RETERR(dst__opensslrsa_init(&dst_t_func[DST_ALG_RSASHA512],
+ DST_ALG_RSASHA512));
+#if defined(HAVE_OPENSSL_DSA) && !defined(PK11_DSA_DISABLE)
+ RETERR(dst__openssldsa_init(&dst_t_func[DST_ALG_DSA]));
+ RETERR(dst__openssldsa_init(&dst_t_func[DST_ALG_NSEC3DSA]));
+#endif
+#ifndef PK11_DH_DISABLE
+ RETERR(dst__openssldh_init(&dst_t_func[DST_ALG_DH]));
+#endif
+#ifdef HAVE_OPENSSL_GOST
+ RETERR(dst__opensslgost_init(&dst_t_func[DST_ALG_ECCGOST]));
+#endif
+#ifdef HAVE_OPENSSL_ECDSA
+ RETERR(dst__opensslecdsa_init(&dst_t_func[DST_ALG_ECDSA256]));
+ RETERR(dst__opensslecdsa_init(&dst_t_func[DST_ALG_ECDSA384]));
+#endif
+#ifdef HAVE_OPENSSL_ED25519
+ RETERR(dst__openssleddsa_init(&dst_t_func[DST_ALG_ED25519]));
+#endif
+#ifdef HAVE_OPENSSL_ED448
+ RETERR(dst__openssleddsa_init(&dst_t_func[DST_ALG_ED448]));
+#endif
+#elif PKCS11CRYPTO
+ RETERR(dst__pkcs11_init(mctx, engine));
+#ifndef PK11_MD5_DISABLE
+ RETERR(dst__pkcs11rsa_init(&dst_t_func[DST_ALG_RSAMD5]));
+#endif
+ RETERR(dst__pkcs11rsa_init(&dst_t_func[DST_ALG_RSASHA1]));
+ RETERR(dst__pkcs11rsa_init(&dst_t_func[DST_ALG_NSEC3RSASHA1]));
+ RETERR(dst__pkcs11rsa_init(&dst_t_func[DST_ALG_RSASHA256]));
+ RETERR(dst__pkcs11rsa_init(&dst_t_func[DST_ALG_RSASHA512]));
+#ifndef PK11_DSA_DISABLE
+ RETERR(dst__pkcs11dsa_init(&dst_t_func[DST_ALG_DSA]));
+ RETERR(dst__pkcs11dsa_init(&dst_t_func[DST_ALG_NSEC3DSA]));
+#endif
+#ifndef PK11_DH_DISABLE
+ RETERR(dst__pkcs11dh_init(&dst_t_func[DST_ALG_DH]));
+#endif
+#ifdef HAVE_PKCS11_ECDSA
+ RETERR(dst__pkcs11ecdsa_init(&dst_t_func[DST_ALG_ECDSA256]));
+ RETERR(dst__pkcs11ecdsa_init(&dst_t_func[DST_ALG_ECDSA384]));
+#endif
+#ifdef HAVE_PKCS11_ED25519
+ RETERR(dst__pkcs11eddsa_init(&dst_t_func[DST_ALG_ED25519]));
+#endif
+#ifdef HAVE_PKCS11_ED448
+ RETERR(dst__pkcs11eddsa_init(&dst_t_func[DST_ALG_ED448]));
+#endif
+#ifdef HAVE_PKCS11_GOST
+ RETERR(dst__pkcs11gost_init(&dst_t_func[DST_ALG_ECCGOST]));
+#endif
+#endif /* if OPENSSL, elif PKCS11CRYPTO */
+#ifdef GSSAPI
+ RETERR(dst__gssapi_init(&dst_t_func[DST_ALG_GSSAPI]));
+#endif
+ dst_initialized = true;
+ return (ISC_R_SUCCESS);
+
+ out:
+ /* avoid immediate crash! */
+ dst_initialized = true;
+ dst_lib_destroy();
+ return (result);
+}
+
+void
+dst_lib_destroy(void) {
+ int i;
+ RUNTIME_CHECK(dst_initialized == true);
+ dst_initialized = 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();
+#elif PKCS11CRYPTO
+ (void) dst__pkcs11_destroy();
+#endif /* if OPENSSL, elif PKCS11CRYPTO */
+ if (dst__memory_pool != NULL)
+ isc_mem_detach(&dst__memory_pool);
+ if (dst_entropy_pool != NULL)
+ isc_entropy_detach(&dst_entropy_pool);
+}
+
+bool
+dst_algorithm_supported(unsigned int alg) {
+ REQUIRE(dst_initialized == true);
+
+ if (alg >= DST_MAX_ALGS || dst_t_func[alg] == NULL)
+ return (false);
+ return (true);
+}
+
+bool
+dst_ds_digest_supported(unsigned int digest_type) {
+ return (digest_type == DNS_DSDIGEST_SHA1 ||
+ digest_type == DNS_DSDIGEST_SHA256 ||
+#if defined(HAVE_OPENSSL_GOST) || defined(HAVE_PKCS11_GOST)
+ digest_type == DNS_DSDIGEST_GOST ||
+#endif
+ digest_type == DNS_DSDIGEST_SHA384);
+}
+
+isc_result_t
+dst_context_create(dst_key_t *key, isc_mem_t *mctx, dst_context_t **dctxp) {
+ return (dst_context_create4(key, mctx, DNS_LOGCATEGORY_GENERAL,
+ true, 0, dctxp));
+}
+
+isc_result_t
+dst_context_create2(dst_key_t *key, isc_mem_t *mctx,
+ isc_logcategory_t *category, dst_context_t **dctxp)
+{
+ return (dst_context_create4(key, mctx, category, true, 0, dctxp));
+}
+
+isc_result_t
+dst_context_create3(dst_key_t *key, isc_mem_t *mctx,
+ isc_logcategory_t *category, bool useforsigning,
+ dst_context_t **dctxp)
+{
+ return (dst_context_create4(key, mctx, category,
+ useforsigning, 0, dctxp));
+}
+
+isc_result_t
+dst_context_create4(dst_key_t *key, isc_mem_t *mctx,
+ isc_logcategory_t *category, bool useforsigning,
+ int maxbits, dst_context_t **dctxp)
+{
+ dst_context_t *dctx;
+ isc_result_t result;
+
+ REQUIRE(dst_initialized == true);
+ REQUIRE(VALID_KEY(key));
+ REQUIRE(mctx != NULL);
+ REQUIRE(dctxp != NULL && *dctxp == NULL);
+
+ if (key->func->createctx == NULL &&
+ key->func->createctx2 == 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);
+ memset(dctx, 0, sizeof(*dctx));
+ dst_key_attach(key, &dctx->key);
+ isc_mem_attach(mctx, &dctx->mctx);
+ dctx->category = category;
+ if (useforsigning)
+ dctx->use = DO_SIGN;
+ else
+ dctx->use = DO_VERIFY;
+ if (key->func->createctx2 != NULL)
+ result = key->func->createctx2(key, maxbits, dctx);
+ else
+ result = key->func->createctx(key, dctx);
+ if (result != ISC_R_SUCCESS) {
+ if (dctx->key != NULL)
+ dst_key_free(&dctx->key);
+ isc_mem_putanddetach(&dctx->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);
+ if (dctx->key != NULL)
+ dst_key_free(&dctx->key);
+ dctx->magic = 0;
+ isc_mem_putanddetach(&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) == 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_context_verify2(dst_context_t *dctx, unsigned int maxbits,
+ 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 &&
+ dctx->key->func->verify2 == NULL)
+ return (DST_R_NOTPUBLICKEY);
+
+ return (dctx->key->func->verify2 != NULL ?
+ dctx->key->func->verify2(dctx, maxbits, sig) :
+ 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 == 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) == 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 == 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);
+}
+
+void
+dst_key_setexternal(dst_key_t *key, bool value) {
+ key->external = value;
+}
+
+bool
+dst_key_isexternal(dst_key_t *key) {
+ return (key->external);
+}
+
+isc_result_t
+dst_key_getfilename(dns_name_t *name, dns_keytag_t id,
+ unsigned int alg, int type, const char *directory,
+ isc_mem_t *mctx, isc_buffer_t *buf)
+{
+ isc_result_t result;
+
+ REQUIRE(dst_initialized == true);
+ REQUIRE(dns_name_isabsolute(name));
+ REQUIRE((type & (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC)) != 0);
+ REQUIRE(mctx != NULL);
+ REQUIRE(buf != NULL);
+
+ CHECKALG(alg);
+
+ result = buildfilename(name, id, alg, type, directory, buf);
+ if (result == ISC_R_SUCCESS) {
+ if (isc_buffer_availablelength(buf) > 0)
+ isc_buffer_putuint8(buf, 0);
+ else
+ result = ISC_R_NOSPACE;
+ }
+
+ return (result);
+}
+
+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)
+{
+ isc_result_t result;
+ char filename[ISC_DIR_NAMEMAX];
+ isc_buffer_t buf;
+ dst_key_t *key;
+
+ REQUIRE(dst_initialized == 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);
+
+ key = NULL;
+
+ isc_buffer_init(&buf, filename, ISC_DIR_NAMEMAX);
+ result = dst_key_getfilename(name, id, alg, type, NULL, mctx, &buf);
+ if (result != ISC_R_SUCCESS)
+ goto out;
+
+ result = dst_key_fromnamedfile(filename, directory, type, mctx, &key);
+ if (result != ISC_R_SUCCESS)
+ goto out;
+
+ result = computeid(key);
+ if (result != ISC_R_SUCCESS)
+ goto out;
+
+ if (!dns_name_equal(name, key->key_name) || id != key->key_id ||
+ alg != key->key_alg) {
+ result = DST_R_INVALIDPRIVATEKEY;
+ goto out;
+ }
+
+ *keyp = key;
+ result = ISC_R_SUCCESS;
+
+ out:
+ if ((key != NULL) && (result != ISC_R_SUCCESS))
+ dst_key_free(&key);
+
+ return (result);
+}
+
+isc_result_t
+dst_key_fromnamedfile(const char *filename, const char *dirname,
+ int type, isc_mem_t *mctx, dst_key_t **keyp)
+{
+ isc_result_t result;
+ dst_key_t *pubkey = NULL, *key = NULL;
+ char *newfilename = NULL;
+ int newfilenamelen = 0;
+ isc_lex_t *lex = NULL;
+
+ REQUIRE(dst_initialized == true);
+ REQUIRE(filename != NULL);
+ REQUIRE((type & (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC)) != 0);
+ REQUIRE(mctx != NULL);
+ REQUIRE(keyp != NULL && *keyp == NULL);
+
+ /* If an absolute path is specified, don't use the key directory */
+#ifndef WIN32
+ if (filename[0] == '/')
+ dirname = NULL;
+#else /* WIN32 */
+ if (filename[0] == '/' || filename[0] == '\\')
+ dirname = NULL;
+#endif
+
+ newfilenamelen = strlen(filename) + 5;
+ if (dirname != NULL)
+ newfilenamelen += strlen(dirname) + 1;
+ newfilename = isc_mem_get(mctx, newfilenamelen);
+ if (newfilename == NULL)
+ return (ISC_R_NOMEMORY);
+ result = addsuffix(newfilename, newfilenamelen,
+ dirname, filename, ".key");
+ INSIST(result == ISC_R_SUCCESS);
+
+ result = dst_key_read_public(newfilename, type, mctx, &pubkey);
+ isc_mem_put(mctx, newfilename, newfilenamelen);
+ newfilename = NULL;
+ RETERR(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, pubkey->key_ttl, mctx);
+ if (key == NULL) {
+ dst_key_free(&pubkey);
+ return (ISC_R_NOMEMORY);
+ }
+
+ if (key->func->parse == NULL)
+ RETERR(DST_R_UNSUPPORTEDALG);
+
+ newfilenamelen = strlen(filename) + 9;
+ if (dirname != NULL)
+ newfilenamelen += strlen(dirname) + 1;
+ newfilename = isc_mem_get(mctx, newfilenamelen);
+ if (newfilename == NULL)
+ RETERR(ISC_R_NOMEMORY);
+ result = addsuffix(newfilename, newfilenamelen,
+ dirname, 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, pubkey));
+ isc_lex_destroy(&lex);
+
+ RETERR(computeid(key));
+
+ if (pubkey->key_id != key->key_id)
+ RETERR(DST_R_INVALIDPRIVATEKEY);
+ dst_key_free(&pubkey);
+
+ *keyp = key;
+ return (ISC_R_SUCCESS);
+
+ out:
+ if (pubkey != NULL)
+ dst_key_free(&pubkey);
+ if (newfilename != NULL)
+ isc_mem_put(mctx, newfilename, newfilenamelen);
+ if (lex != NULL)
+ isc_lex_destroy(&lex);
+ if (key != NULL)
+ dst_key_free(&key);
+ return (result);
+}
+
+isc_result_t
+dst_key_todns(const dst_key_t *key, isc_buffer_t *target) {
+ REQUIRE(dst_initialized == 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, (uint16_t)(key->key_flags & 0xffff));
+ isc_buffer_putuint8(target, (uint8_t)key->key_proto);
+ isc_buffer_putuint8(target, (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,
+ (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)
+{
+ uint8_t alg, proto;
+ uint32_t flags, extflags;
+ dst_key_t *key = NULL;
+ dns_keytag_t id, rid;
+ 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);
+ rid = dst_region_computerid(&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;
+ key->key_rid = rid;
+
+ *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 == 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 == 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, NULL));
+ 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, isc_region_t *intoken)
+{
+ dst_key_t *key;
+ isc_result_t result;
+
+ REQUIRE(gssctx != NULL);
+ REQUIRE(keyp != NULL && *keyp == NULL);
+
+ key = get_key_struct(name, DST_ALG_GSSAPI, 0, DNS_KEYPROTO_DNSSEC,
+ 0, dns_rdataclass_in, 0, mctx);
+ if (key == NULL)
+ return (ISC_R_NOMEMORY);
+
+ if (intoken != NULL) {
+ /*
+ * Keep the token for use by external ssu rules. They may need
+ * to examine the PAC in the kerberos ticket.
+ */
+ RETERR(isc_buffer_allocate(key->mctx, &key->key_tkeytoken,
+ intoken->length));
+ RETERR(isc_buffer_copyregion(key->key_tkeytoken, intoken));
+ }
+
+ key->keydata.gssctx = gssctx;
+ *keyp = key;
+ result = ISC_R_SUCCESS;
+out:
+ return result;
+}
+
+isc_result_t
+dst_key_buildinternal(dns_name_t *name, unsigned int alg,
+ unsigned int bits, unsigned int flags,
+ unsigned int protocol, dns_rdataclass_t rdclass,
+ void *data, isc_mem_t *mctx, dst_key_t **keyp)
+{
+ dst_key_t *key;
+ isc_result_t result;
+
+ REQUIRE(dst_initialized == true);
+ REQUIRE(dns_name_isabsolute(name));
+ REQUIRE(mctx != NULL);
+ REQUIRE(keyp != NULL && *keyp == NULL);
+ REQUIRE(data != NULL);
+
+ CHECKALG(alg);
+
+ key = get_key_struct(name, alg, flags, protocol, bits, rdclass,
+ 0, mctx);
+ if (key == NULL)
+ return (ISC_R_NOMEMORY);
+
+ key->keydata.generic = data;
+
+ 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_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 == 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, 0, 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)
+{
+ return (dst_key_generate2(name, alg, bits, param, flags, protocol,
+ rdclass, mctx, keyp, NULL));
+}
+
+isc_result_t
+dst_key_generate2(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,
+ void (*callback)(int))
+{
+ dst_key_t *key;
+ isc_result_t ret;
+
+ REQUIRE(dst_initialized == 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, 0, 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, callback);
+ 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_result_t
+dst_key_getnum(const dst_key_t *key, int type, uint32_t *valuep)
+{
+ REQUIRE(VALID_KEY(key));
+ REQUIRE(valuep != NULL);
+ REQUIRE(type <= DST_MAX_NUMERIC);
+ if (!key->numset[type])
+ return (ISC_R_NOTFOUND);
+ *valuep = key->nums[type];
+ return (ISC_R_SUCCESS);
+}
+
+void
+dst_key_setnum(dst_key_t *key, int type, uint32_t value)
+{
+ REQUIRE(VALID_KEY(key));
+ REQUIRE(type <= DST_MAX_NUMERIC);
+ key->nums[type] = value;
+ key->numset[type] = true;
+}
+
+void
+dst_key_unsetnum(dst_key_t *key, int type)
+{
+ REQUIRE(VALID_KEY(key));
+ REQUIRE(type <= DST_MAX_NUMERIC);
+ key->numset[type] = false;
+}
+
+isc_result_t
+dst_key_gettime(const dst_key_t *key, int type, isc_stdtime_t *timep) {
+ REQUIRE(VALID_KEY(key));
+ REQUIRE(timep != NULL);
+ REQUIRE(type <= DST_MAX_TIMES);
+ if (!key->timeset[type])
+ return (ISC_R_NOTFOUND);
+ *timep = key->times[type];
+ return (ISC_R_SUCCESS);
+}
+
+void
+dst_key_settime(dst_key_t *key, int type, isc_stdtime_t when) {
+ REQUIRE(VALID_KEY(key));
+ REQUIRE(type <= DST_MAX_TIMES);
+ key->times[type] = when;
+ key->timeset[type] = true;
+}
+
+void
+dst_key_unsettime(dst_key_t *key, int type) {
+ REQUIRE(VALID_KEY(key));
+ REQUIRE(type <= DST_MAX_TIMES);
+ key->timeset[type] = false;
+}
+
+isc_result_t
+dst_key_getprivateformat(const dst_key_t *key, int *majorp, int *minorp) {
+ REQUIRE(VALID_KEY(key));
+ REQUIRE(majorp != NULL);
+ REQUIRE(minorp != NULL);
+ *majorp = key->fmt_major;
+ *minorp = key->fmt_minor;
+ return (ISC_R_SUCCESS);
+}
+
+void
+dst_key_setprivateformat(dst_key_t *key, int major, int minor) {
+ REQUIRE(VALID_KEY(key));
+ key->fmt_major = major;
+ key->fmt_minor = minor;
+}
+
+static bool
+comparekeys(const dst_key_t *key1, const dst_key_t *key2,
+ bool match_revoked_key,
+ bool (*compare)(const dst_key_t *key1,
+ const dst_key_t *key2))
+{
+ REQUIRE(dst_initialized == true);
+ REQUIRE(VALID_KEY(key1));
+ REQUIRE(VALID_KEY(key2));
+
+ if (key1 == key2)
+ return (true);
+
+ if (key1->key_alg != key2->key_alg)
+ return (false);
+
+ if (key1->key_id != key2->key_id) {
+ if (!match_revoked_key)
+ return (false);
+#ifndef PK11_MD5_DISABLE
+ if (key1->key_alg == DST_ALG_RSAMD5)
+ return (false);
+#endif
+ if ((key1->key_flags & DNS_KEYFLAG_REVOKE) ==
+ (key2->key_flags & DNS_KEYFLAG_REVOKE))
+ return (false);
+ if (key1->key_id != key2->key_rid &&
+ key1->key_rid != key2->key_id)
+ return (false);
+ }
+
+ if (compare != NULL)
+ return (compare(key1, key2));
+ else
+ return (false);
+}
+
+
+/*
+ * Compares only the public portion of two keys, by converting them
+ * both to wire format and comparing the results.
+ */
+static bool
+pub_compare(const dst_key_t *key1, const dst_key_t *key2) {
+ isc_result_t result;
+ unsigned char buf1[DST_KEY_MAXSIZE], buf2[DST_KEY_MAXSIZE];
+ isc_buffer_t b1, b2;
+ isc_region_t r1, r2;
+
+ isc_buffer_init(&b1, buf1, sizeof(buf1));
+ result = dst_key_todns(key1, &b1);
+ if (result != ISC_R_SUCCESS)
+ return (false);
+ /* Zero out flags. */
+ buf1[0] = buf1[1] = 0;
+ if ((key1->key_flags & DNS_KEYFLAG_EXTENDED) != 0)
+ isc_buffer_subtract(&b1, 2);
+
+ isc_buffer_init(&b2, buf2, sizeof(buf2));
+ result = dst_key_todns(key2, &b2);
+ if (result != ISC_R_SUCCESS)
+ return (false);
+ /* Zero out flags. */
+ buf2[0] = buf2[1] = 0;
+ if ((key2->key_flags & DNS_KEYFLAG_EXTENDED) != 0)
+ isc_buffer_subtract(&b2, 2);
+
+ isc_buffer_usedregion(&b1, &r1);
+ /* Remove extended flags. */
+ if ((key1->key_flags & DNS_KEYFLAG_EXTENDED) != 0) {
+ memmove(&buf1[4], &buf1[6], r1.length - 6);
+ r1.length -= 2;
+ }
+
+ isc_buffer_usedregion(&b2, &r2);
+ /* Remove extended flags. */
+ if ((key2->key_flags & DNS_KEYFLAG_EXTENDED) != 0) {
+ memmove(&buf2[4], &buf2[6], r2.length - 6);
+ r2.length -= 2;
+ }
+ return (isc_region_compare(&r1, &r2) == 0);
+}
+
+bool
+dst_key_compare(const dst_key_t *key1, const dst_key_t *key2) {
+ return (comparekeys(key1, key2, false, key1->func->compare));
+}
+
+bool
+dst_key_pubcompare(const dst_key_t *key1, const dst_key_t *key2,
+ bool match_revoked_key)
+{
+ return (comparekeys(key1, key2, match_revoked_key, pub_compare));
+}
+
+
+bool
+dst_key_paramcompare(const dst_key_t *key1, const dst_key_t *key2) {
+ REQUIRE(dst_initialized == true);
+ REQUIRE(VALID_KEY(key1));
+ REQUIRE(VALID_KEY(key2));
+
+ if (key1 == key2)
+ return (true);
+ if (key1->key_alg == key2->key_alg &&
+ key1->func->paramcompare != NULL &&
+ key1->func->paramcompare(key1, key2) == true)
+ return (true);
+ else
+ return (false);
+}
+
+void
+dst_key_attach(dst_key_t *source, dst_key_t **target) {
+
+ REQUIRE(dst_initialized == true);
+ REQUIRE(target != NULL && *target == NULL);
+ REQUIRE(VALID_KEY(source));
+
+ isc_refcount_increment(&source->refs, NULL);
+ *target = source;
+}
+
+void
+dst_key_free(dst_key_t **keyp) {
+ isc_mem_t *mctx;
+ dst_key_t *key;
+ unsigned int refs;
+
+ REQUIRE(dst_initialized == true);
+ REQUIRE(keyp != NULL && VALID_KEY(*keyp));
+
+ key = *keyp;
+ mctx = key->mctx;
+
+ isc_refcount_decrement(&key->refs, &refs);
+ if (refs != 0)
+ return;
+
+ isc_refcount_destroy(&key->refs);
+ 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));
+ if (key->key_tkeytoken) {
+ isc_buffer_free(&key->key_tkeytoken);
+ }
+ isc_safe_memwipe(key, sizeof(*key));
+ isc_mem_putanddetach(&mctx, key, sizeof(*key));
+ *keyp = NULL;
+}
+
+bool
+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 == true);
+ REQUIRE(VALID_KEY(key));
+ REQUIRE(n != NULL);
+
+ /* XXXVIX this switch statement is too sparse to gen a jump table. */
+ switch (key->key_alg) {
+#ifndef PK11_MD5_DISABLE
+ case DST_ALG_RSAMD5:
+#endif
+ case DST_ALG_RSASHA1:
+ case DST_ALG_NSEC3RSASHA1:
+ case DST_ALG_RSASHA256:
+ case DST_ALG_RSASHA512:
+ *n = (key->key_size + 7) / 8;
+ break;
+#ifndef PK11_DSA_DISABLE
+ case DST_ALG_DSA:
+ case DST_ALG_NSEC3DSA:
+ *n = DNS_SIG_DSASIGSIZE;
+ break;
+#endif
+ case DST_ALG_ECCGOST:
+ *n = DNS_SIG_GOSTSIGSIZE;
+ break;
+ case DST_ALG_ECDSA256:
+ *n = DNS_SIG_ECDSA256SIZE;
+ break;
+ case DST_ALG_ECDSA384:
+ *n = DNS_SIG_ECDSA384SIZE;
+ break;
+ case DST_ALG_ED25519:
+ *n = DNS_SIG_ED25519SIZE;
+ break;
+ case DST_ALG_ED448:
+ *n = DNS_SIG_ED448SIZE;
+ break;
+#ifndef PK11_MD5_DISABLE
+ case DST_ALG_HMACMD5:
+ *n = 16;
+ break;
+#endif
+ 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;
+#ifndef PK11_DH_DISABLE
+ case DST_ALG_DH:
+#endif
+ 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 == true);
+ REQUIRE(VALID_KEY(key));
+ REQUIRE(n != NULL);
+
+#ifndef PK11_DH_DISABLE
+ if (key->key_alg == DST_ALG_DH)
+ *n = (key->key_size + 7) / 8;
+ else
+#endif
+ return (DST_R_UNSUPPORTEDALG);
+#ifndef PK11_DH_DISABLE
+ return (ISC_R_SUCCESS);
+#endif
+}
+
+/*%
+ * Set the flags on a key, then recompute the key ID
+ */
+isc_result_t
+dst_key_setflags(dst_key_t *key, uint32_t flags) {
+ REQUIRE(VALID_KEY(key));
+ key->key_flags = flags;
+ return (computeid(key));
+}
+
+void
+dst_key_format(const dst_key_t *key, char *cp, unsigned int size) {
+ char namestr[DNS_NAME_FORMATSIZE];
+ char algstr[DNS_NAME_FORMATSIZE];
+
+ dns_name_format(dst_key_name(key), namestr, sizeof(namestr));
+ dns_secalg_format((dns_secalg_t) dst_key_alg(key), algstr,
+ sizeof(algstr));
+ snprintf(cp, size, "%s/%s/%d", namestr, algstr, dst_key_id(key));
+}
+
+isc_result_t
+dst_key_dump(dst_key_t *key, isc_mem_t *mctx, char **buffer, int *length) {
+
+ REQUIRE(buffer != NULL && *buffer == NULL);
+ REQUIRE(length != NULL && *length == 0);
+ REQUIRE(VALID_KEY(key));
+
+ if (key->func->dump == NULL)
+ return (ISC_R_NOTIMPLEMENTED);
+ return (key->func->dump(key, mctx, buffer, length));
+}
+
+isc_result_t
+dst_key_restore(dns_name_t *name, unsigned int alg, unsigned int flags,
+ unsigned int protocol, dns_rdataclass_t rdclass,
+ isc_mem_t *mctx, const char *keystr, dst_key_t **keyp)
+{
+ isc_result_t result;
+ dst_key_t *key;
+
+ REQUIRE(dst_initialized == true);
+ REQUIRE(keyp != NULL && *keyp == NULL);
+
+ if (alg >= DST_MAX_ALGS || dst_t_func[alg] == NULL)
+ return (DST_R_UNSUPPORTEDALG);
+
+ if (dst_t_func[alg]->restore == NULL)
+ return (ISC_R_NOTIMPLEMENTED);
+
+ key = get_key_struct(name, alg, flags, protocol, 0, rdclass, 0, mctx);
+ if (key == NULL)
+ return (ISC_R_NOMEMORY);
+
+ result = (dst_t_func[alg]->restore)(key, keystr);
+ if (result == ISC_R_SUCCESS)
+ *keyp = key;
+ else
+ dst_key_free(&key);
+
+ return (result);
+}
+
+/***
+ *** 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,
+ dns_ttl_t ttl, isc_mem_t *mctx)
+{
+ dst_key_t *key;
+ isc_result_t result;
+ int i;
+
+ 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->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);
+ }
+
+ result = isc_refcount_init(&key->refs, 1);
+ if (result != ISC_R_SUCCESS) {
+ dns_name_free(key->key_name, mctx);
+ isc_mem_put(mctx, key->key_name, sizeof(dns_name_t));
+ isc_mem_put(mctx, key, sizeof(dst_key_t));
+ return (NULL);
+ }
+ isc_mem_attach(mctx, &key->mctx);
+ key->key_alg = alg;
+ key->key_flags = flags;
+ key->key_proto = protocol;
+ key->keydata.generic = NULL;
+ key->key_size = bits;
+ key->key_class = rdclass;
+ key->key_ttl = ttl;
+ key->func = dst_t_func[alg];
+ key->fmt_major = 0;
+ key->fmt_minor = 0;
+ for (i = 0; i < (DST_MAX_TIMES + 1); i++) {
+ key->times[i] = 0;
+ key->timeset[i] = false;
+ }
+ key->inactive = false;
+ key->magic = KEY_MAGIC;
+ return (key);
+}
+
+bool
+dst_key_inactive(const dst_key_t *key) {
+
+ REQUIRE(VALID_KEY(key));
+
+ return (key->inactive);
+}
+
+void
+dst_key_setinactive(dst_key_t *key, bool inactive) {
+
+ REQUIRE(VALID_KEY(key));
+
+ key->inactive = inactive;
+}
+
+/*%
+ * 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;
+ uint32_t ttl = 0;
+ 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,
+ 0, NULL);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup;
+
+ /* Read the next word: either TTL, class, or 'KEY' */
+ NEXTTOKEN(lex, opt, &token);
+
+ if (token.type != isc_tokentype_string)
+ BADTOKEN();
+
+ /* 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,
+ 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;
+
+ dst_key_setttl(*keyp, ttl);
+
+ cleanup:
+ if (lex != NULL)
+ isc_lex_destroy(&lex);
+ return (ret);
+}
+
+static bool
+issymmetric(const dst_key_t *key) {
+ REQUIRE(dst_initialized == true);
+ REQUIRE(VALID_KEY(key));
+
+ /* XXXVIX this switch statement is too sparse to gen a jump table. */
+ switch (key->key_alg) {
+#ifndef PK11_MD5_DISABLE
+ case DST_ALG_RSAMD5:
+#endif
+ case DST_ALG_RSASHA1:
+ case DST_ALG_NSEC3RSASHA1:
+ case DST_ALG_RSASHA256:
+ case DST_ALG_RSASHA512:
+#ifndef PK11_DSA_DISABLE
+ case DST_ALG_DSA:
+ case DST_ALG_NSEC3DSA:
+#endif
+#ifndef PK11_DH_DISABLE
+ case DST_ALG_DH:
+#endif
+ case DST_ALG_ECCGOST:
+ case DST_ALG_ECDSA256:
+ case DST_ALG_ECDSA384:
+ case DST_ALG_ED25519:
+ case DST_ALG_ED448:
+ return (false);
+#ifndef PK11_MD5_DISABLE
+ case DST_ALG_HMACMD5:
+#endif
+ case DST_ALG_HMACSHA1:
+ case DST_ALG_HMACSHA224:
+ case DST_ALG_HMACSHA256:
+ case DST_ALG_HMACSHA384:
+ case DST_ALG_HMACSHA512:
+ case DST_ALG_GSSAPI:
+ return (true);
+ default:
+ return (false);
+ }
+}
+
+/*%
+ * Write key timing metadata to a file pointer, preceded by 'tag'
+ */
+static void
+printtime(const dst_key_t *key, int type, const char *tag, FILE *stream) {
+ isc_result_t result;
+#ifdef ISC_PLATFORM_USETHREADS
+ char output[26]; /* Minimum buffer as per ctime_r() specification. */
+#else
+ const char *output;
+#endif
+ isc_stdtime_t when;
+ time_t t;
+ char utc[sizeof("YYYYMMDDHHSSMM")];
+ isc_buffer_t b;
+ isc_region_t r;
+
+ result = dst_key_gettime(key, type, &when);
+ if (result == ISC_R_NOTFOUND)
+ return;
+
+ /* time_t and isc_stdtime_t might be different sizes */
+ t = when;
+#ifdef ISC_PLATFORM_USETHREADS
+#ifdef WIN32
+ if (ctime_s(output, sizeof(output), &t) != 0)
+ goto error;
+#else
+ if (ctime_r(&t, output) == NULL)
+ goto error;
+#endif
+#else
+ output = ctime(&t);
+#endif
+
+ isc_buffer_init(&b, utc, sizeof(utc));
+ result = dns_time32_totext(when, &b);
+ if (result != ISC_R_SUCCESS)
+ goto error;
+
+ isc_buffer_usedregion(&b, &r);
+ fprintf(stream, "%s: %.*s (%.*s)\n", tag, (int)r.length, r.base,
+ (int)strlen(output) - 1, output);
+ return;
+
+ error:
+ fprintf(stream, "%s: (set, unable to display)\n", tag);
+}
+
+/*%
+ * 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);
+ }
+
+ /* Write key information in comments */
+ if ((type & DST_TYPE_KEY) == 0) {
+ fprintf(fp, "; This is a %s%s-signing key, keyid %d, for ",
+ (key->key_flags & DNS_KEYFLAG_REVOKE) != 0 ?
+ "revoked " :
+ "",
+ (key->key_flags & DNS_KEYFLAG_KSK) != 0 ?
+ "key" :
+ "zone",
+ key->key_id);
+ ret = dns_name_print(key->key_name, fp);
+ if (ret != ISC_R_SUCCESS) {
+ fclose(fp);
+ return (ret);
+ }
+ fputc('\n', fp);
+
+ printtime(key, DST_TIME_CREATED, "; Created", fp);
+ printtime(key, DST_TIME_PUBLISH, "; Publish", fp);
+ printtime(key, DST_TIME_ACTIVATE, "; Activate", fp);
+ printtime(key, DST_TIME_REVOKE, "; Revoke", fp);
+ printtime(key, DST_TIME_INACTIVE, "; Inactive", fp);
+ printtime(key, DST_TIME_DELETE, "; Delete", fp);
+ printtime(key, DST_TIME_SYNCPUBLISH , "; SyncPublish", fp);
+ printtime(key, DST_TIME_SYNCDELETE , "; SyncDelete", fp);
+ }
+
+ /* Now print the actual key */
+ ret = dns_name_print(key->key_name, fp);
+ fprintf(fp, " ");
+
+ if (key->key_ttl != 0)
+ fprintf(fp, "%u ", key->key_ttl);
+
+ isc_buffer_usedregion(&classb, &r);
+ if ((unsigned) fwrite(r.base, 1, r.length, fp) != r.length)
+ ret = DST_R_WRITEERROR;
+
+ if ((type & DST_TYPE_KEY) != 0)
+ fprintf(fp, " KEY ");
+ else
+ fprintf(fp, " DNSKEY ");
+
+ isc_buffer_usedregion(&textb, &r);
+ if ((unsigned) fwrite(r.base, 1, r.length, fp) != r.length)
+ ret = DST_R_WRITEERROR;
+
+ fputc('\n', fp);
+ fflush(fp);
+ if (ferror(fp))
+ ret = DST_R_WRITEERROR;
+ fclose(fp);
+
+ return (ret);
+}
+
+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, 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);
+ snprintf((char *) isc_buffer_used(out),
+ (int)isc_buffer_availablelength(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);
+ key->key_rid = dst_region_computerid(&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, 0, 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 == true);
+
+ if (dst_algorithm_supported(alg))
+ return (ISC_R_SUCCESS);
+#if !defined(OPENSSL) && !defined(PKCS11CRYPTO)
+ 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 ||
+ alg == DST_ALG_RSASHA256 || alg == DST_ALG_RSASHA512 ||
+ alg == DST_ALG_ECCGOST ||
+ alg == DST_ALG_ECDSA256 || alg == DST_ALG_ECDSA384 ||
+ alg == DST_ALG_ED25519 || alg == DST_ALG_ED448)
+ return (DST_R_NOCRYPTO);
+#endif
+ return (DST_R_UNSUPPORTEDALG);
+}
+
+static isc_result_t
+addsuffix(char *filename, int len, const char *odirname,
+ 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;
+
+ if (odirname == NULL)
+ n = snprintf(filename, len, "%.*s%s", olen, ofilename, suffix);
+ else
+ n = snprintf(filename, len, "%s/%.*s%s",
+ odirname, olen, ofilename, suffix);
+ if (n < 0)
+ return (ISC_R_FAILURE);
+ if (n >= len)
+ return (ISC_R_NOSPACE);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dst__entropy_getdata(void *buf, unsigned int len, bool pseudo) {
+ unsigned int flags = dst_entropy_flags;
+
+ if (dst_entropy_pool == NULL)
+ return (ISC_R_FAILURE);
+
+ if (len == 0)
+ return (ISC_R_SUCCESS);
+
+#ifdef PKCS11CRYPTO
+ UNUSED(pseudo);
+ UNUSED(flags);
+ return (pk11_rand_bytes(buf, len));
+#else /* PKCS11CRYPTO */
+ if (pseudo)
+ flags &= ~ISC_ENTROPY_GOODONLY;
+ else
+ flags |= ISC_ENTROPY_BLOCKING;
+ return (isc_entropy_getdata(dst_entropy_pool, buf, len, NULL, flags));
+#endif /* PKCS11CRYPTO */
+}
+
+unsigned int
+dst__entropy_status(void) {
+#ifndef PKCS11CRYPTO
+#ifdef GSSAPI
+ unsigned int flags = dst_entropy_flags;
+ isc_result_t ret;
+ unsigned char buf[32];
+ static bool first = true;
+
+ if (dst_entropy_pool == NULL)
+ return (0);
+
+ if (first) {
+ /* Someone believes RAND_status() initializes the PRNG */
+ flags &= ~ISC_ENTROPY_GOODONLY;
+ ret = isc_entropy_getdata(dst_entropy_pool, buf,
+ sizeof(buf), NULL, flags);
+ INSIST(ret == ISC_R_SUCCESS);
+ isc_entropy_putdata(dst_entropy_pool, buf,
+ sizeof(buf), 2 * sizeof(buf));
+ first = false;
+ }
+#endif
+ return (isc_entropy_status(dst_entropy_pool));
+#else
+ return (0);
+#endif
+}
+
+isc_buffer_t *
+dst_key_tkeytoken(const dst_key_t *key) {
+ REQUIRE(VALID_KEY(key));
+ return (key->key_tkeytoken);
+}
diff --git a/lib/dns/dst_gost.h b/lib/dns/dst_gost.h
new file mode 100644
index 0000000..e42c6a1
--- /dev/null
+++ b/lib/dns/dst_gost.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef DST_GOST_H
+#define DST_GOST_H 1
+
+#include <isc/lang.h>
+#include <isc/log.h>
+#include <dst/result.h>
+
+#define ISC_GOST_DIGESTLENGTH 32U
+
+#ifdef HAVE_OPENSSL_GOST
+#include <openssl/evp.h>
+
+typedef struct {
+ EVP_MD_CTX *ctx;
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ EVP_MD_CTX _ctx;
+#endif
+} isc_gost_t;
+
+#endif
+#ifdef HAVE_PKCS11_GOST
+#include <pk11/pk11.h>
+
+typedef pk11_context_t isc_gost_t;
+#endif
+
+ISC_LANG_BEGINDECLS
+
+#if defined(HAVE_OPENSSL_GOST) || defined(HAVE_PKCS11_GOST)
+
+isc_result_t
+isc_gost_init(isc_gost_t *ctx);
+
+void
+isc_gost_invalidate(isc_gost_t *ctx);
+
+isc_result_t
+isc_gost_update(isc_gost_t *ctx, const unsigned char *data, unsigned int len);
+
+isc_result_t
+isc_gost_final(isc_gost_t *ctx, unsigned char *digest);
+
+ISC_LANG_ENDDECLS
+
+#endif /* HAVE_OPENSSL_GOST || HAVE_PKCS11_GOST */
+
+#endif /* DST_GOST_H */
diff --git a/lib/dns/dst_internal.h b/lib/dns/dst_internal.h
new file mode 100644
index 0000000..11e5fc6
--- /dev/null
+++ b/lib/dns/dst_internal.h
@@ -0,0 +1,306 @@
+/*
+ * Portions Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Portions Copyright (C) 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.
+ */
+
+
+#ifndef DST_DST_INTERNAL_H
+#define DST_DST_INTERNAL_H 1
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/lang.h>
+#include <isc/buffer.h>
+#include <isc/magic.h>
+#include <isc/region.h>
+#include <isc/types.h>
+#include <isc/md5.h>
+#include <isc/refcount.h>
+#include <isc/sha1.h>
+#include <isc/sha2.h>
+#include <isc/stdtime.h>
+#include <isc/hmacmd5.h>
+#include <isc/hmacsha.h>
+
+#include <pk11/site.h>
+
+#include <dns/time.h>
+
+#include <dst/dst.h>
+
+#ifdef OPENSSL
+#ifndef PK11_DH_DISABLE
+#include <openssl/dh.h>
+#endif
+#ifndef PK11_DSA_DISABLE
+#include <openssl/dsa.h>
+#endif
+#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)
+
+LIBDNS_EXTERNAL_DATA extern isc_mem_t *dst__memory_pool;
+
+/***
+ *** Types
+ ***/
+
+typedef struct dst_func dst_func_t;
+
+#ifndef PK11_MD5_DISABLE
+typedef struct dst_hmacmd5_key dst_hmacmd5_key_t;
+#endif
+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;
+
+/*%
+ * Indicate whether a DST context will be used for signing
+ * or for verification
+ */
+typedef enum { DO_SIGN, DO_VERIFY } dst_use_t;
+
+/*% DST Key Structure */
+struct dst_key {
+ unsigned int magic;
+ isc_refcount_t refs;
+ 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 */
+ uint32_t key_flags; /*%< flags of the public key */
+ uint16_t key_id; /*%< identifier of the key */
+ uint16_t key_rid; /*%< identifier of the key when
+ revoked */
+ uint16_t key_bits; /*%< hmac digest bits */
+ dns_rdataclass_t key_class; /*%< class of the key record */
+ dns_ttl_t key_ttl; /*%< default/initial dnskey ttl */
+ 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 !defined(USE_EVP) || !USE_EVP
+ RSA *rsa;
+#endif
+#ifndef PK11_DSA_DISABLE
+ DSA *dsa;
+#endif
+#ifndef PK11_DH_DISABLE
+ DH *dh;
+#endif
+ EVP_PKEY *pkey;
+#elif PKCS11CRYPTO
+ pk11_object_t *pkey;
+#endif
+#ifndef PK11_MD5_DISABLE
+ dst_hmacmd5_key_t *hmacmd5;
+#endif
+ 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 */
+
+ isc_stdtime_t times[DST_MAX_TIMES + 1]; /*%< timing metadata */
+ bool timeset[DST_MAX_TIMES + 1]; /*%< data set? */
+ isc_stdtime_t nums[DST_MAX_NUMERIC + 1]; /*%< numeric metadata */
+ bool numset[DST_MAX_NUMERIC + 1]; /*%< data set? */
+ bool inactive; /*%< private key not present as it is
+ inactive */
+ bool external; /*%< external key */
+
+ int fmt_major; /*%< private key format, major version */
+ int fmt_minor; /*%< private key format, minor version */
+
+ dst_func_t * func; /*%< crypto package specific functions */
+ isc_buffer_t *key_tkeytoken; /*%< TKEY token data */
+};
+
+struct dst_context {
+ unsigned int magic;
+ dst_use_t use;
+ dst_key_t *key;
+ isc_mem_t *mctx;
+ isc_logcategory_t *category;
+ union {
+ void *generic;
+ dst_gssapi_signverifyctx_t *gssctx;
+#ifndef PK11_MD5_DISABLE
+ isc_md5_t *md5ctx;
+#endif
+ isc_sha1_t *sha1ctx;
+ isc_sha256_t *sha256ctx;
+ isc_sha512_t *sha512ctx;
+#ifndef PK11_MD5_DISABLE
+ isc_hmacmd5_t *hmacmd5ctx;
+#endif
+ 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;
+#elif PKCS11CRYPTO
+ pk11_context_t *pk11_ctx;
+#endif
+ } ctxdata;
+};
+
+struct dst_func {
+ /*
+ * Context functions
+ */
+ isc_result_t (*createctx)(dst_key_t *key, dst_context_t *dctx);
+ isc_result_t (*createctx2)(dst_key_t *key, int maxbits,
+ 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 (*verify2)(dst_context_t *dctx, int maxbits,
+ const isc_region_t *sig);
+ isc_result_t (*computesecret)(const dst_key_t *pub,
+ const dst_key_t *priv,
+ isc_buffer_t *secret);
+ bool (*compare)(const dst_key_t *key1, const dst_key_t *key2);
+ bool (*paramcompare)(const dst_key_t *key1,
+ const dst_key_t *key2);
+ isc_result_t (*generate)(dst_key_t *key, int parms,
+ void (*callback)(int));
+ bool (*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,
+ dst_key_t *pub);
+
+ /* cleanup */
+ void (*cleanup)(void);
+
+ isc_result_t (*fromlabel)(dst_key_t *key, const char *engine,
+ const char *label, const char *pin);
+ isc_result_t (*dump)(dst_key_t *key, isc_mem_t *mctx, char **buffer,
+ int *length);
+ isc_result_t (*restore)(dst_key_t *key, const char *keystr);
+};
+
+/*%
+ * Initializers
+ */
+isc_result_t dst__openssl_init(const char *engine);
+#define dst__pkcs11_init pk11_initialize
+
+#ifndef PK11_MD5_DISABLE
+isc_result_t dst__hmacmd5_init(struct dst_func **funcp);
+#endif
+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,
+ unsigned char algorithm);
+isc_result_t dst__pkcs11rsa_init(struct dst_func **funcp);
+#ifndef PK11_DSA_DISABLE
+isc_result_t dst__openssldsa_init(struct dst_func **funcp);
+isc_result_t dst__pkcs11dsa_init(struct dst_func **funcp);
+#endif
+#ifndef PK11_DH_DISABLE
+isc_result_t dst__openssldh_init(struct dst_func **funcp);
+isc_result_t dst__pkcs11dh_init(struct dst_func **funcp);
+#endif
+isc_result_t dst__gssapi_init(struct dst_func **funcp);
+#ifdef HAVE_OPENSSL_ECDSA
+isc_result_t dst__opensslecdsa_init(struct dst_func **funcp);
+#endif
+#if defined(HAVE_OPENSSL_ED25519) || defined(HAVE_OPENSSL_ED448)
+isc_result_t dst__openssleddsa_init(struct dst_func **funcp);
+#endif
+#ifdef HAVE_PKCS11_ECDSA
+isc_result_t dst__pkcs11ecdsa_init(struct dst_func **funcp);
+#endif
+#if defined(HAVE_PKCS11_ED25519) || defined(HAVE_PKCS11_ED448)
+isc_result_t dst__pkcs11eddsa_init(struct dst_func **funcp);
+#endif
+#ifdef HAVE_OPENSSL_GOST
+isc_result_t dst__opensslgost_init(struct dst_func **funcp);
+#endif
+#ifdef HAVE_PKCS11_GOST
+isc_result_t dst__pkcs11gost_init(struct dst_func **funcp);
+#endif
+
+/*%
+ * Destructors
+ */
+void dst__openssl_destroy(void);
+#define dst__pkcs11_destroy pk11_finalize
+
+/*%
+ * 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,
+ bool 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..4edf728
--- /dev/null
+++ b/lib/dns/dst_lib.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \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..e085f11
--- /dev/null
+++ b/lib/dns/dst_openssl.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef DST_OPENSSL_H
+#define DST_OPENSSL_H 1
+
+#include <isc/lang.h>
+#include <isc/log.h>
+#include <isc/result.h>
+
+#include <openssl/err.h>
+#include <openssl/rand.h>
+#include <openssl/evp.h>
+#include <openssl/conf.h>
+#include <openssl/crypto.h>
+#include <openssl/bn.h>
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+/*
+ * These are new in OpenSSL 1.1.0. BN_GENCB _cb needs to be declared in
+ * the function like this before the BN_GENCB_new call:
+ *
+ * #if OPENSSL_VERSION_NUMBER < 0x10100000L
+ * _cb;
+ * #endif
+ */
+#define BN_GENCB_free(x) ((void)0)
+#define BN_GENCB_new() (&_cb)
+#define BN_GENCB_get_arg(x) ((x)->arg)
+#endif
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+/*
+ * EVP_dss1() is a version of EVP_sha1() that was needed prior to
+ * 1.1.0 because there was a link between digests and signing algorithms;
+ * the link has been eliminated and EVP_sha1() can be used now instead.
+ */
+#define EVP_dss1 EVP_sha1
+#endif
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+dst__openssl_toresult(isc_result_t fallback);
+
+isc_result_t
+dst__openssl_toresult2(const char *funcname, isc_result_t fallback);
+
+isc_result_t
+dst__openssl_toresult3(isc_logcategory_t *category,
+ const char *funcname, isc_result_t fallback);
+
+#if !defined(OPENSSL_NO_ENGINE)
+ENGINE *
+dst__openssl_getengine(const char *engine);
+#else
+#define dst__openssl_getengine(x) NULL
+#endif
+
+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..f31c33d
--- /dev/null
+++ b/lib/dns/dst_parse.c
@@ -0,0 +1,856 @@
+/*
+ * Portions Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Portions Copyright (C) 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.
+ */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/base64.h>
+#include <isc/dir.h>
+#include <isc/file.h>
+#include <isc/fsaccess.h>
+#include <isc/lex.h>
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/stdtime.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <pk11/site.h>
+
+#include <dns/time.h>
+#include <dns/log.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:"
+
+#define TIMING_NTAGS (DST_MAX_TIMES + 1)
+static const char *timetags[TIMING_NTAGS] = {
+ "Created:",
+ "Publish:",
+ "Activate:",
+ "Revoke:",
+ "Inactive:",
+ "Delete:",
+ "DSPublish:",
+ "SyncPublish:",
+ "SyncDelete:"
+};
+
+#define NUMERIC_NTAGS (DST_MAX_NUMERIC + 1)
+static const char *numerictags[NUMERIC_NTAGS] = {
+ "Predecessor:",
+ "Successor:",
+ "MaxTTL:",
+ "RollPeriod:"
+};
+
+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:" },
+
+#ifndef PK11_DH_DISABLE
+ {TAG_DH_PRIME, "Prime(p):"},
+ {TAG_DH_GENERATOR, "Generator(g):"},
+ {TAG_DH_PRIVATE, "Private_value(x):"},
+ {TAG_DH_PUBLIC, "Public_value(y):"},
+#endif
+
+#ifndef PK11_DSA_DISABLE
+ {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):"},
+#endif
+
+ {TAG_GOST_PRIVASN1, "GostAsn1:"},
+ {TAG_GOST_PRIVRAW, "PrivateKey:"},
+
+ {TAG_ECDSA_PRIVATEKEY, "PrivateKey:"},
+ {TAG_ECDSA_ENGINE, "Engine:" },
+ {TAG_ECDSA_LABEL, "Label:" },
+
+ {TAG_EDDSA_PRIVATEKEY, "PrivateKey:"},
+ {TAG_EDDSA_ENGINE, "Engine:" },
+ {TAG_EDDSA_LABEL, "Label:" },
+
+#ifndef PK11_MD5_DISABLE
+ {TAG_HMACMD5_KEY, "Key:"},
+ {TAG_HMACMD5_BITS, "Bits:"},
+#endif
+
+ {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; map[i].tag != NULL; i++) {
+ if (strcasecmp(s, map[i].tag) == 0 &&
+ (TAG_ALG(map[i].value) == alg))
+ return (map[i].value);
+ }
+ return (-1);
+}
+
+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
+find_metadata(const char *s, const char *tags[], int ntags) {
+ int i;
+
+ for (i = 0; i < ntags; i++) {
+ if (tags[i] != NULL && strcasecmp(s, tags[i]) == 0)
+ return (i);
+ }
+
+ return (-1);
+}
+
+static int
+find_timedata(const char *s) {
+ return (find_metadata(s, timetags, TIMING_NTAGS));
+}
+
+static int
+find_numericdata(const char *s) {
+ return (find_metadata(s, numerictags, NUMERIC_NTAGS));
+}
+
+static int
+check_rsa(const dst_private_t *priv, bool external) {
+ int i, j;
+ bool have[RSA_NTAGS];
+ bool ok;
+ unsigned int mask;
+
+ if (external)
+ return ((priv->nelements == 0) ? 0 : -1);
+
+ for (i = 0; i < RSA_NTAGS; i++)
+ have[i] = 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] = true;
+ }
+
+ mask = (1ULL << TAG_SHIFT) - 1;
+
+ 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 );
+}
+
+#ifndef PK11_DH_DISABLE
+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);
+}
+#endif
+
+#ifndef PK11_DSA_DISABLE
+static int
+check_dsa(const dst_private_t *priv, bool external) {
+ int i, j;
+
+ if (external)
+ return ((priv->nelements == 0)? 0 : -1);
+
+ 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);
+}
+#endif
+
+static int
+check_gost(const dst_private_t *priv, bool external) {
+
+ if (external)
+ return ((priv->nelements == 0)? 0 : -1);
+
+ if (priv->nelements != GOST_NTAGS)
+ return (-1);
+ if ((priv->elements[0].tag != TAG(DST_ALG_ECCGOST, 0)) &&
+ (priv->elements[0].tag != TAG(DST_ALG_ECCGOST, 1)))
+ return (-1);
+ return (0);
+}
+
+static int
+check_ecdsa(const dst_private_t *priv, bool external) {
+ int i, j;
+ bool have[ECDSA_NTAGS];
+ bool ok;
+ unsigned int mask;
+
+ if (external)
+ return ((priv->nelements == 0) ? 0 : -1);
+
+ for (i = 0; i < ECDSA_NTAGS; i++)
+ have[i] = false;
+ for (j = 0; j < priv->nelements; j++) {
+ for (i = 0; i < ECDSA_NTAGS; i++)
+ if (priv->elements[j].tag == TAG(DST_ALG_ECDSA256, i))
+ break;
+ if (i == ECDSA_NTAGS)
+ return (-1);
+ have[i] = true;
+ }
+
+ mask = (1ULL << TAG_SHIFT) - 1;
+
+ if (have[TAG_ECDSA_ENGINE & mask])
+ ok = have[TAG_ECDSA_LABEL & mask];
+ else
+ ok = have[TAG_ECDSA_PRIVATEKEY & mask];
+ return (ok ? 0 : -1 );
+}
+
+static int
+check_eddsa(const dst_private_t *priv, bool external) {
+ int i, j;
+ bool have[EDDSA_NTAGS];
+ bool ok;
+ unsigned int mask;
+
+ if (external)
+ return ((priv->nelements == 0) ? 0 : -1);
+
+ for (i = 0; i < EDDSA_NTAGS; i++)
+ have[i] = false;
+ for (j = 0; j < priv->nelements; j++) {
+ for (i = 0; i < EDDSA_NTAGS; i++)
+ if (priv->elements[j].tag == TAG(DST_ALG_ED25519, i))
+ break;
+ if (i == EDDSA_NTAGS)
+ return (-1);
+ have[i] = true;
+ }
+
+ mask = (1ULL << TAG_SHIFT) - 1;
+
+ if (have[TAG_EDDSA_ENGINE & mask])
+ ok = have[TAG_EDDSA_LABEL & mask];
+ else
+ ok = have[TAG_EDDSA_PRIVATEKEY & mask];
+ return (ok ? 0 : -1 );
+}
+
+#ifndef PK11_MD5_DISABLE
+static int
+check_hmac_md5(const dst_private_t *priv, bool 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);
+}
+#endif
+
+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,
+ bool old, bool external)
+{
+#ifdef PK11_MD5_DISABLE
+ UNUSED(old);
+#endif
+ /* XXXVIX this switch statement is too sparse to gen a jump table. */
+ switch (alg) {
+#ifndef PK11_MD5_DISABLE
+ case DST_ALG_RSAMD5:
+#endif
+ case DST_ALG_RSASHA1:
+ case DST_ALG_NSEC3RSASHA1:
+ case DST_ALG_RSASHA256:
+ case DST_ALG_RSASHA512:
+ return (check_rsa(priv, external));
+#ifndef PK11_DH_DISABLE
+ case DST_ALG_DH:
+ return (check_dh(priv));
+#endif
+#ifndef PK11_DSA_DISABLE
+ case DST_ALG_DSA:
+ case DST_ALG_NSEC3DSA:
+ return (check_dsa(priv, external));
+#endif
+ case DST_ALG_ECCGOST:
+ return (check_gost(priv, external));
+ case DST_ALG_ECDSA256:
+ case DST_ALG_ECDSA384:
+ return (check_ecdsa(priv, external));
+ case DST_ALG_ED25519:
+ case DST_ALG_ED448:
+ return (check_eddsa(priv, external));
+#ifndef PK11_MD5_DISABLE
+ case DST_ALG_HMACMD5:
+ return (check_hmac_md5(priv, old));
+#endif
+ 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;
+}
+
+isc_result_t
+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, check;
+ isc_buffer_t b;
+ isc_token_t token;
+ unsigned char *data = NULL;
+ unsigned int opt = ISC_LEXOPT_EOL;
+ isc_stdtime_t when;
+ isc_result_t ret;
+ bool external = false;
+
+ 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 > DST_MAJOR_VERSION) {
+ ret = DST_R_INVALIDPRIVATEKEY;
+ goto fail;
+ }
+
+ /*
+ * Store the private key format version number
+ */
+ dst_key_setprivateformat(key, major, minor);
+
+ 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;
+ }
+
+ if (strcmp(DST_AS_STR(token), "External:") == 0) {
+ external = true;
+ goto next;
+ }
+
+ /* Numeric metadata */
+ tag = find_numericdata(DST_AS_STR(token));
+ if (tag >= 0) {
+ INSIST(tag < NUMERIC_NTAGS);
+
+ NEXTTOKEN(lex, opt | ISC_LEXOPT_NUMBER, &token);
+ if (token.type != isc_tokentype_number) {
+ ret = DST_R_INVALIDPRIVATEKEY;
+ goto fail;
+ }
+
+ dst_key_setnum(key, tag, token.value.as_ulong);
+ goto next;
+ }
+
+ /* Timing metadata */
+ tag = find_timedata(DST_AS_STR(token));
+ if (tag >= 0) {
+ INSIST(tag < TIMING_NTAGS);
+
+ NEXTTOKEN(lex, opt, &token);
+ if (token.type != isc_tokentype_string) {
+ ret = DST_R_INVALIDPRIVATEKEY;
+ goto fail;
+ }
+
+ ret = dns_time32_fromtext(DST_AS_STR(token), &when);
+ if (ret != ISC_R_SUCCESS)
+ goto fail;
+
+ dst_key_settime(key, tag, when);
+
+ goto next;
+ }
+
+ /* Key data */
+ tag = find_value(DST_AS_STR(token), alg);
+ if (tag < 0 && minor > DST_MINOR_VERSION)
+ goto next;
+ else if (tag < 0) {
+ 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;
+ priv->nelements++;
+
+ next:
+ READLINE(lex, opt, &token);
+ data = NULL;
+ }
+
+ done:
+ if (external && priv->nelements != 0) {
+ ret = DST_R_INVALIDPRIVATEKEY;
+ goto fail;
+ }
+
+#ifdef PK11_MD5_DISABLE
+ check = check_data(priv, alg == DST_ALG_RSA ? DST_ALG_RSASHA1 : alg,
+ true, external);
+#else
+ check = check_data(priv, alg, true, external);
+#endif
+ if (check < 0) {
+ ret = DST_R_INVALIDPRIVATEKEY;
+ goto fail;
+ } else if (check != ISC_R_SUCCESS) {
+ ret = check;
+ goto fail;
+ }
+
+ key->external = external;
+
+ return (ISC_R_SUCCESS);
+
+fail:
+ dst__privstruct_free(priv, mctx);
+ if (data != NULL)
+ isc_mem_put(mctx, data, MAXFIELDSIZE);
+
+ return (ret);
+}
+
+isc_result_t
+dst__privstruct_writefile(const dst_key_t *key, const dst_private_t *priv,
+ const char *directory)
+{
+ FILE *fp;
+ isc_result_t result;
+ char filename[ISC_DIR_NAMEMAX];
+ char buffer[MAXFIELDSIZE * 2];
+ isc_fsaccess_t access;
+ isc_stdtime_t when;
+ uint32_t value;
+ isc_buffer_t b;
+ isc_region_t r;
+ int major, minor;
+ mode_t mode;
+ int i, ret;
+
+ REQUIRE(priv != NULL);
+
+ ret = check_data(priv, dst_key_alg(key), false, key->external);
+ if (ret < 0)
+ return (DST_R_INVALIDPRIVATEKEY);
+ else if (ret != ISC_R_SUCCESS)
+ return (ret);
+
+ isc_buffer_init(&b, filename, sizeof(filename));
+ result = dst_key_buildfilename(key, DST_TYPE_PRIVATE, directory, &b);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = isc_file_mode(filename, &mode);
+ if (result == ISC_R_SUCCESS && mode != 0600) {
+ /* File exists; warn that we are changing its permissions */
+ int level;
+
+#ifdef _WIN32
+ /* Windows security model is pretty different,
+ * e.g., there is no umask... */
+ level = ISC_LOG_NOTICE;
+#else
+ level = ISC_LOG_WARNING;
+#endif
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_DNSSEC, level,
+ "Permissions on the file %s "
+ "have changed from 0%o to 0600 as "
+ "a result of this operation.",
+ filename, (unsigned int)mode);
+ }
+
+ 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);
+
+ dst_key_getprivateformat(key, &major, &minor);
+ if (major == 0 && minor == 0) {
+ major = DST_MAJOR_VERSION;
+ minor = DST_MINOR_VERSION;
+ }
+
+ /* XXXDCL return value should be checked for full filesystem */
+ fprintf(fp, "%s v%d.%d\n", PRIVATE_KEY_STR, major, minor);
+
+ fprintf(fp, "%s %u ", 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_NSEC3RSASHA1:
+ fprintf(fp, "(NSEC3RSASHA1)\n");
+ break;
+ case DST_ALG_NSEC3DSA:
+ fprintf(fp, "(NSEC3DSA)\n");
+ break;
+ case DST_ALG_RSASHA256:
+ fprintf(fp, "(RSASHA256)\n");
+ break;
+ case DST_ALG_RSASHA512:
+ fprintf(fp, "(RSASHA512)\n");
+ break;
+ case DST_ALG_ECCGOST:
+ fprintf(fp, "(ECC-GOST)\n");
+ break;
+ case DST_ALG_ECDSA256:
+ fprintf(fp, "(ECDSAP256SHA256)\n");
+ break;
+ case DST_ALG_ECDSA384:
+ fprintf(fp, "(ECDSAP384SHA384)\n");
+ break;
+ case DST_ALG_ED25519:
+ fprintf(fp, "(ED25519)\n");
+ break;
+ case DST_ALG_ED448:
+ fprintf(fp, "(ED448)\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++) {
+ 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));
+ result = isc_base64_totext(&r, sizeof(buffer), "", &b);
+ if (result != ISC_R_SUCCESS) {
+ fclose(fp);
+ return (DST_R_INVALIDPRIVATEKEY);
+ }
+ isc_buffer_usedregion(&b, &r);
+
+ fprintf(fp, "%s %.*s\n", s, (int)r.length, r.base);
+ }
+
+ if (key->external)
+ fprintf(fp, "External:\n");
+
+ /* Add the metadata tags */
+ if (major > 1 || (major == 1 && minor >= 3)) {
+ for (i = 0; i < NUMERIC_NTAGS; i++) {
+ result = dst_key_getnum(key, i, &value);
+ if (result != ISC_R_SUCCESS)
+ continue;
+ fprintf(fp, "%s %u\n", numerictags[i], value);
+ }
+ for (i = 0; i < TIMING_NTAGS; i++) {
+ result = dst_key_gettime(key, i, &when);
+ if (result != ISC_R_SUCCESS)
+ continue;
+
+ isc_buffer_init(&b, buffer, sizeof(buffer));
+ result = dns_time32_totext(when, &b);
+ if (result != ISC_R_SUCCESS) {
+ fclose(fp);
+ return (DST_R_INVALIDPRIVATEKEY);
+ }
+
+ isc_buffer_usedregion(&b, &r);
+
+ fprintf(fp, "%s %.*s\n", timetags[i], (int)r.length,
+ r.base);
+ }
+ }
+
+ fflush(fp);
+ result = ferror(fp) ? DST_R_WRITEERROR : ISC_R_SUCCESS;
+ fclose(fp);
+ return (result);
+}
+
+/*! \file */
diff --git a/lib/dns/dst_parse.h b/lib/dns/dst_parse.h
new file mode 100644
index 0000000..a2600e1
--- /dev/null
+++ b/lib/dns/dst_parse.h
@@ -0,0 +1,142 @@
+/*
+ * Portions Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Portions Copyright (C) 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.
+ */
+
+
+/*! \file */
+#ifndef DST_DST_PARSE_H
+#define DST_DST_PARSE_H 1
+
+#include <isc/lang.h>
+
+#include <dst/dst.h>
+
+#define MAXFIELDSIZE 512
+
+/*
+ * Maximum number of fields in a private file is 18 (12 algorithm-
+ * specific fields for RSA, plus 6 generic fields).
+ */
+#define MAXFIELDS 12+6
+
+#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 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 GOST_NTAGS 1
+#define TAG_GOST_PRIVASN1 ((DST_ALG_ECCGOST << TAG_SHIFT) + 0)
+#define TAG_GOST_PRIVRAW ((DST_ALG_ECCGOST << TAG_SHIFT) + 1)
+
+#define ECDSA_NTAGS 4
+#define TAG_ECDSA_PRIVATEKEY ((DST_ALG_ECDSA256 << TAG_SHIFT) + 0)
+#define TAG_ECDSA_ENGINE ((DST_ALG_ECDSA256 << TAG_SHIFT) + 1)
+#define TAG_ECDSA_LABEL ((DST_ALG_ECDSA256 << TAG_SHIFT) + 2)
+
+#define EDDSA_NTAGS 4
+#define TAG_EDDSA_PRIVATEKEY ((DST_ALG_ED25519 << TAG_SHIFT) + 0)
+#define TAG_EDDSA_ENGINE ((DST_ALG_ED25519 << TAG_SHIFT) + 1)
+#define TAG_EDDSA_LABEL ((DST_ALG_ED25519 << TAG_SHIFT) + 2)
+
+#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);
+
+isc_result_t
+dst__privstruct_parse(dst_key_t *key, unsigned int alg, isc_lex_t *lex,
+ isc_mem_t *mctx, dst_private_t *priv);
+
+isc_result_t
+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_pkcs11.h b/lib/dns/dst_pkcs11.h
new file mode 100644
index 0000000..b8011f2
--- /dev/null
+++ b/lib/dns/dst_pkcs11.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef DST_PKCS11_H
+#define DST_PKCS11_H 1
+
+#include <isc/lang.h>
+#include <isc/log.h>
+#include <isc/result.h>
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+dst__pkcs11_toresult(const char *funcname, const char *file, int line,
+ isc_result_t fallback, CK_RV rv);
+
+#define PK11_CALL(func, args, fallback) \
+ ((void) (((rv = (func) args) == CKR_OK) || \
+ ((ret = dst__pkcs11_toresult(#func, __FILE__, __LINE__, \
+ fallback, rv)), 0)))
+
+#define PK11_RET(func, args, fallback) \
+ ((void) (((rv = (func) args) == CKR_OK) || \
+ ((ret = dst__pkcs11_toresult(#func, __FILE__, __LINE__, \
+ fallback, rv)), 0))); \
+ if (rv != CKR_OK) goto err;
+
+ISC_LANG_ENDDECLS
+
+#endif /* DST_PKCS11_H */
diff --git a/lib/dns/dst_result.c b/lib/dns/dst_result.c
new file mode 100644
index 0000000..caee5f0
--- /dev/null
+++ b/lib/dns/dst_result.c
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#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 */
+ "crypto 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 */
+ "external key", /*%< 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 */
+ "illegal operation for an external key",/*%< 22 */
+};
+
+static const char *ids[DST_R_NRESULTS] = {
+ "DST_R_UNSUPPORTEDALG",
+ "DST_R_CRYPTOFAILURE",
+ "DST_R_NOCRYPTO",
+ "DST_R_NULLKEY",
+ "DST_R_INVALIDPUBLICKEY",
+ "DST_R_INVALIDPRIVATEKEY",
+ "UNUSED",
+ "DST_R_WRITEERROR",
+ "DST_R_INVALIDPARAM",
+ "UNUSED",
+ "UNUSED",
+ "DST_R_SIGNFAILURE",
+ "UNUSED",
+ "UNUSED",
+ "DST_R_VERIFYFAILURE",
+ "DST_R_NOTPUBLICKEY",
+ "DST_R_NOTPRIVATEKEY",
+ "DST_R_KEYCANNOTCOMPUTESECRET",
+ "DST_R_COMPUTESECRETFAILURE",
+ "DST_R_NORANDOMNESS",
+ "DST_R_BADKEYTYPE",
+ "DST_R_NOENGINE",
+ "DST_R_EXTERNALKEY",
+};
+
+#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);
+ result = isc_result_registerids(ISC_RESULTCLASS_DST, DST_R_NRESULTS,
+ ids, dst_msgcat, DST_RESULT_RESULTSET);
+ if (result != ISC_R_SUCCESS)
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "isc_result_registerids() 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/dyndb.c b/lib/dns/dyndb.c
new file mode 100644
index 0000000..93ad795
--- /dev/null
+++ b/lib/dns/dyndb.c
@@ -0,0 +1,467 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#include <config.h>
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#elif _WIN32
+#include <windows.h>
+#endif
+
+#include <isc/buffer.h>
+#include <isc/mem.h>
+#include <isc/mutex.h>
+#include <isc/once.h>
+#include <isc/result.h>
+#include <isc/region.h>
+#include <isc/task.h>
+#include <isc/types.h>
+#include <isc/util.h>
+
+#include <dns/dyndb.h>
+#include <dns/log.h>
+#include <dns/types.h>
+#include <dns/view.h>
+#include <dns/zone.h>
+
+#include <string.h>
+
+#define CHECK(op) \
+ do { result = (op); \
+ if (result != ISC_R_SUCCESS) goto cleanup; \
+ } while (0)
+
+
+typedef struct dyndb_implementation dyndb_implementation_t;
+struct dyndb_implementation {
+ isc_mem_t *mctx;
+ void *handle;
+ dns_dyndb_register_t *register_func;
+ dns_dyndb_destroy_t *destroy_func;
+ char *name;
+ void *inst;
+ LINK(dyndb_implementation_t) link;
+};
+
+/*
+ * List of dyndb implementations. Locked by dyndb_lock.
+ *
+ * These are stored here so they can be cleaned up on shutdown.
+ * (The order in which they are stored is not important.)
+ */
+static LIST(dyndb_implementation_t) dyndb_implementations;
+
+/* Locks dyndb_implementations. */
+static isc_mutex_t dyndb_lock;
+static isc_once_t once = ISC_ONCE_INIT;
+
+static void
+dyndb_initialize(void) {
+ RUNTIME_CHECK(isc_mutex_init(&dyndb_lock) == ISC_R_SUCCESS);
+ INIT_LIST(dyndb_implementations);
+}
+
+static dyndb_implementation_t *
+impfind(const char *name) {
+ dyndb_implementation_t *imp;
+
+ for (imp = ISC_LIST_HEAD(dyndb_implementations);
+ imp != NULL;
+ imp = ISC_LIST_NEXT(imp, link))
+ if (strcasecmp(name, imp->name) == 0)
+ return (imp);
+ return (NULL);
+}
+
+#if HAVE_DLFCN_H && HAVE_DLOPEN
+static isc_result_t
+load_symbol(void *handle, const char *filename,
+ const char *symbol_name, void **symbolp)
+{
+ const char *errmsg;
+ void *symbol;
+
+ REQUIRE(handle != NULL);
+ REQUIRE(symbolp != NULL && *symbolp == NULL);
+
+ symbol = dlsym(handle, symbol_name);
+ if (symbol == NULL) {
+ errmsg = dlerror();
+ if (errmsg == NULL)
+ errmsg = "returned function pointer is NULL";
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_DYNDB, ISC_LOG_ERROR,
+ "failed to lookup symbol %s in "
+ "dyndb module '%s': %s",
+ symbol_name, filename, errmsg);
+ return (ISC_R_FAILURE);
+ }
+ dlerror();
+
+ *symbolp = symbol;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+load_library(isc_mem_t *mctx, const char *filename, const char *instname,
+ dyndb_implementation_t **impp)
+{
+ isc_result_t result;
+ void *handle = NULL;
+ dyndb_implementation_t *imp = NULL;
+ dns_dyndb_register_t *register_func = NULL;
+ dns_dyndb_destroy_t *destroy_func = NULL;
+ dns_dyndb_version_t *version_func = NULL;
+ int version, flags;
+
+ REQUIRE(impp != NULL && *impp == NULL);
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_DYNDB, ISC_LOG_INFO,
+ "loading DynDB instance '%s' driver '%s'",
+ instname, filename);
+
+ flags = RTLD_NOW|RTLD_LOCAL;
+#ifdef RTLD_DEEPBIND
+ flags |= RTLD_DEEPBIND;
+#endif
+
+ handle = dlopen(filename, flags);
+ if (handle == NULL)
+ CHECK(ISC_R_FAILURE);
+
+ /* Clear dlerror */
+ dlerror();
+
+ CHECK(load_symbol(handle, filename, "dyndb_version",
+ (void **)&version_func));
+
+ version = version_func(NULL);
+ if (version < (DNS_DYNDB_VERSION - DNS_DYNDB_AGE) ||
+ version > DNS_DYNDB_VERSION)
+ {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_DYNDB, ISC_LOG_ERROR,
+ "driver API version mismatch: %d/%d",
+ version, DNS_DYNDB_VERSION);
+ CHECK(ISC_R_FAILURE);
+ }
+
+ CHECK(load_symbol(handle, filename, "dyndb_init",
+ (void **)&register_func));
+ CHECK(load_symbol(handle, filename, "dyndb_destroy",
+ (void **)&destroy_func));
+
+ imp = isc_mem_get(mctx, sizeof(dyndb_implementation_t));
+ if (imp == NULL)
+ CHECK(ISC_R_NOMEMORY);
+
+ imp->mctx = NULL;
+ isc_mem_attach(mctx, &imp->mctx);
+ imp->handle = handle;
+ imp->register_func = register_func;
+ imp->destroy_func = destroy_func;
+ imp->name = isc_mem_strdup(mctx, instname);
+ if (imp->name == NULL)
+ CHECK(ISC_R_NOMEMORY);
+
+ imp->inst = NULL;
+ INIT_LINK(imp, link);
+
+ *impp = imp;
+ imp = NULL;
+
+cleanup:
+ if (result != ISC_R_SUCCESS)
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_DYNDB, ISC_LOG_ERROR,
+ "failed to dynamically load instance '%s' "
+ "driver '%s': %s (%s)", instname, filename,
+ dlerror(), isc_result_totext(result));
+ if (imp != NULL)
+ isc_mem_putanddetach(&imp->mctx, imp, sizeof(dyndb_implementation_t));
+ if (result != ISC_R_SUCCESS && handle != NULL)
+ dlclose(handle);
+
+ return (result);
+}
+
+static void
+unload_library(dyndb_implementation_t **impp) {
+ dyndb_implementation_t *imp;
+
+ REQUIRE(impp != NULL && *impp != NULL);
+
+ imp = *impp;
+
+ isc_mem_free(imp->mctx, imp->name);
+ isc_mem_putanddetach(&imp->mctx, imp, sizeof(dyndb_implementation_t));
+
+ *impp = NULL;
+}
+#elif _WIN32
+static isc_result_t
+load_symbol(HMODULE handle, const char *filename,
+ const char *symbol_name, void **symbolp)
+{
+ void *symbol;
+
+ REQUIRE(handle != NULL);
+ REQUIRE(symbolp != NULL && *symbolp == NULL);
+
+ symbol = GetProcAddress(handle, symbol_name);
+ if (symbol == NULL) {
+ int errstatus = GetLastError();
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_DYNDB, ISC_LOG_ERROR,
+ "failed to lookup symbol %s in "
+ "dyndb module '%s': %d",
+ symbol_name, filename, errstatus);
+ return (ISC_R_FAILURE);
+ }
+
+ *symbolp = symbol;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+load_library(isc_mem_t *mctx, const char *filename, const char *instname,
+ dyndb_implementation_t **impp)
+{
+ isc_result_t result;
+ HMODULE handle;
+ dyndb_implementation_t *imp = NULL;
+ dns_dyndb_register_t *register_func = NULL;
+ dns_dyndb_destroy_t *destroy_func = NULL;
+ dns_dyndb_version_t *version_func = NULL;
+ int version;
+
+ REQUIRE(impp != NULL && *impp == NULL);
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_DYNDB, ISC_LOG_INFO,
+ "loading DynDB instance '%s' driver '%s'",
+ instname, filename);
+
+ handle = LoadLibraryA(filename);
+ if (handle == NULL)
+ CHECK(ISC_R_FAILURE);
+
+ CHECK(load_symbol(handle, filename, "dyndb_version",
+ (void **)&version_func));
+
+ version = version_func(NULL);
+ if (version < (DNS_DYNDB_VERSION - DNS_DYNDB_AGE) ||
+ version > DNS_DYNDB_VERSION)
+ {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_DYNDB, ISC_LOG_ERROR,
+ "driver API version mismatch: %d/%d",
+ version, DNS_DYNDB_VERSION);
+ CHECK(ISC_R_FAILURE);
+ }
+
+ CHECK(load_symbol(handle, filename, "dyndb_init",
+ (void **)&register_func));
+ CHECK(load_symbol(handle, filename, "dyndb_destroy",
+ (void **)&destroy_func));
+
+ imp = isc_mem_get(mctx, sizeof(dyndb_implementation_t));
+ if (imp == NULL)
+ CHECK(ISC_R_NOMEMORY);
+
+ imp->mctx = NULL;
+ isc_mem_attach(mctx, &imp->mctx);
+ imp->handle = handle;
+ imp->register_func = register_func;
+ imp->destroy_func = destroy_func;
+ imp->name = isc_mem_strdup(mctx, instname);
+ if (imp->name == NULL)
+ CHECK(ISC_R_NOMEMORY);
+
+ imp->inst = NULL;
+ INIT_LINK(imp, link);
+
+ *impp = imp;
+ imp = NULL;
+
+cleanup:
+ if (result != ISC_R_SUCCESS)
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_DYNDB, ISC_LOG_ERROR,
+ "failed to dynamically load instance '%s' "
+ "driver '%s': %d (%s)", instname, filename,
+ GetLastError(), isc_result_totext(result));
+ if (imp != NULL)
+ isc_mem_putanddetach(&imp->mctx, imp, sizeof(dyndb_implementation_t));
+ if (result != ISC_R_SUCCESS && handle != NULL)
+ FreeLibrary(handle);
+
+ return (result);
+}
+
+static void
+unload_library(dyndb_implementation_t **impp) {
+ dyndb_implementation_t *imp;
+
+ REQUIRE(impp != NULL && *impp != NULL);
+
+ imp = *impp;
+
+ isc_mem_free(imp->mctx, imp->name);
+ isc_mem_putanddetach(&imp->mctx, imp, sizeof(dyndb_implementation_t));
+
+ *impp = NULL;
+}
+#else /* HAVE_DLFCN_H || _WIN32 */
+static isc_result_t
+load_library(isc_mem_t *mctx, const char *filename, const char *instname,
+ dyndb_implementation_t **impp)
+{
+ UNUSED(mctx);
+ UNUSED(filename);
+ UNUSED(instname);
+ UNUSED(impp);
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DYNDB,
+ ISC_LOG_ERROR,
+ "dynamic database support is not implemented");
+
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static void
+unload_library(dyndb_implementation_t **impp)
+{
+ UNUSED(impp);
+}
+#endif /* HAVE_DLFCN_H */
+
+isc_result_t
+dns_dyndb_load(const char *libname, const char *name, const char *parameters,
+ const char *file, unsigned long line, isc_mem_t *mctx,
+ const dns_dyndbctx_t *dctx)
+{
+ isc_result_t result;
+ dyndb_implementation_t *implementation = NULL;
+
+ REQUIRE(DNS_DYNDBCTX_VALID(dctx));
+ REQUIRE(name != NULL);
+
+ RUNTIME_CHECK(isc_once_do(&once, dyndb_initialize) == ISC_R_SUCCESS);
+
+ LOCK(&dyndb_lock);
+
+ /* duplicate instance names are not allowed */
+ if (impfind(name) != NULL)
+ CHECK(ISC_R_EXISTS);
+
+ CHECK(load_library(mctx, libname, name, &implementation));
+ CHECK(implementation->register_func(mctx, name, parameters, file, line,
+ dctx, &implementation->inst));
+
+ APPEND(dyndb_implementations, implementation, link);
+ result = ISC_R_SUCCESS;
+
+cleanup:
+ if (result != ISC_R_SUCCESS)
+ if (implementation != NULL)
+ unload_library(&implementation);
+
+ UNLOCK(&dyndb_lock);
+ return (result);
+}
+
+void
+dns_dyndb_cleanup(bool exiting) {
+ dyndb_implementation_t *elem;
+ dyndb_implementation_t *prev;
+
+ RUNTIME_CHECK(isc_once_do(&once, dyndb_initialize) == ISC_R_SUCCESS);
+
+ LOCK(&dyndb_lock);
+ elem = TAIL(dyndb_implementations);
+ while (elem != NULL) {
+ prev = PREV(elem, link);
+ UNLINK(dyndb_implementations, elem, link);
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_DYNDB, ISC_LOG_INFO,
+ "unloading DynDB instance '%s'", elem->name);
+ elem->destroy_func(&elem->inst);
+ ENSURE(elem->inst == NULL);
+ unload_library(&elem);
+ elem = prev;
+ }
+ UNLOCK(&dyndb_lock);
+
+ if (exiting == true)
+ isc_mutex_destroy(&dyndb_lock);
+}
+
+isc_result_t
+dns_dyndb_createctx(isc_mem_t *mctx, const void *hashinit, isc_log_t *lctx,
+ dns_view_t *view, dns_zonemgr_t *zmgr, isc_task_t *task,
+ isc_timermgr_t *tmgr, dns_dyndbctx_t **dctxp)
+{
+ dns_dyndbctx_t *dctx;
+
+ REQUIRE(dctxp != NULL && *dctxp == NULL);
+
+ dctx = isc_mem_get(mctx, sizeof(*dctx));
+ if (dctx == NULL)
+ return (ISC_R_NOMEMORY);
+
+ memset(dctx, 0, sizeof(*dctx));
+ if (view != NULL)
+ dns_view_attach(view, &dctx->view);
+ if (zmgr != NULL)
+ dns_zonemgr_attach(zmgr, &dctx->zmgr);
+ if (task != NULL)
+ isc_task_attach(task, &dctx->task);
+ dctx->timermgr = tmgr;
+ dctx->hashinit = hashinit;
+ dctx->lctx = lctx;
+ dctx->refvar = &isc_bind9;
+
+ isc_mem_attach(mctx, &dctx->mctx);
+ dctx->magic = DNS_DYNDBCTX_MAGIC;
+
+ *dctxp = dctx;
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_dyndb_destroyctx(dns_dyndbctx_t **dctxp) {
+ dns_dyndbctx_t *dctx;
+
+ REQUIRE(dctxp != NULL && DNS_DYNDBCTX_VALID(*dctxp));
+
+ dctx = *dctxp;
+ *dctxp = NULL;
+
+ dctx->magic = 0;
+
+ if (dctx->view != NULL)
+ dns_view_detach(&dctx->view);
+ if (dctx->zmgr != NULL)
+ dns_zonemgr_detach(&dctx->zmgr);
+ if (dctx->task != NULL)
+ isc_task_detach(&dctx->task);
+ dctx->timermgr = NULL;
+ dctx->lctx = NULL;
+
+ isc_mem_putanddetach(&dctx->mctx, dctx, sizeof(*dctx));
+}
diff --git a/lib/dns/ecdb.c b/lib/dns/ecdb.c
new file mode 100644
index 0000000..cf24d73
--- /dev/null
+++ b/lib/dns/ecdb.c
@@ -0,0 +1,835 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include "config.h"
+
+#include <stdbool.h>
+
+#include <isc/result.h>
+#include <isc/util.h>
+#include <isc/mutex.h>
+#include <isc/mem.h>
+
+#include <dns/db.h>
+#include <dns/ecdb.h>
+#include <dns/rdata.h>
+#include <dns/rdataset.h>
+#include <dns/rdatasetiter.h>
+#include <dns/rdataslab.h>
+
+#define ECDB_MAGIC ISC_MAGIC('E', 'C', 'D', 'B')
+#define VALID_ECDB(db) ((db) != NULL && \
+ (db)->common.impmagic == ECDB_MAGIC)
+
+#define ECDBNODE_MAGIC ISC_MAGIC('E', 'C', 'D', 'N')
+#define VALID_ECDBNODE(ecdbn) ISC_MAGIC_VALID(ecdbn, ECDBNODE_MAGIC)
+
+/*%
+ * The 'ephemeral' cache DB (ecdb) implementation. An ecdb just provides
+ * temporary storage for ongoing name resolution with the common DB interfaces.
+ * It actually doesn't cache anything. The implementation expects any stored
+ * data is released within a short period, and does not care about the
+ * scalability in terms of the number of nodes.
+ */
+
+typedef struct dns_ecdb {
+ /* Unlocked */
+ dns_db_t common;
+ isc_mutex_t lock;
+
+ /* Locked */
+ unsigned int references;
+ ISC_LIST(struct dns_ecdbnode) nodes;
+} dns_ecdb_t;
+
+typedef struct dns_ecdbnode {
+ /* Unlocked */
+ unsigned int magic;
+ isc_mutex_t lock;
+ dns_ecdb_t *ecdb;
+ dns_name_t name;
+ ISC_LINK(struct dns_ecdbnode) link;
+
+ /* Locked */
+ ISC_LIST(struct rdatasetheader) rdatasets;
+ unsigned int references;
+} dns_ecdbnode_t;
+
+typedef struct rdatasetheader {
+ dns_rdatatype_t type;
+ dns_ttl_t ttl;
+ dns_trust_t trust;
+ dns_rdatatype_t covers;
+ unsigned int attributes;
+
+ ISC_LINK(struct rdatasetheader) link;
+} rdatasetheader_t;
+
+/* Copied from rbtdb.c */
+#define RDATASET_ATTR_NXDOMAIN 0x0010
+#define RDATASET_ATTR_NEGATIVE 0x0100
+#define NXDOMAIN(header) \
+ (((header)->attributes & RDATASET_ATTR_NXDOMAIN) != 0)
+#define NEGATIVE(header) \
+ (((header)->attributes & RDATASET_ATTR_NEGATIVE) != 0)
+
+static isc_result_t dns_ecdb_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 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 void rdataset_settrust(dns_rdataset_t *rdataset, dns_trust_t trust);
+
+static dns_rdatasetmethods_t rdataset_methods = {
+ rdataset_disassociate,
+ rdataset_first,
+ rdataset_next,
+ rdataset_current,
+ rdataset_clone,
+ rdataset_count,
+ NULL, /* addnoqname */
+ NULL, /* getnoqname */
+ NULL, /* addclosest */
+ NULL, /* getclosest */
+ NULL, /* getadditional */
+ NULL, /* setadditional */
+ NULL, /* putadditional */
+ rdataset_settrust, /* settrust */
+ NULL, /* expire */
+ NULL, /* clearprefetch */
+ NULL, /* setownercase */
+ NULL /* getownercase */
+};
+
+typedef struct ecdb_rdatasetiter {
+ dns_rdatasetiter_t common;
+ rdatasetheader_t *current;
+} ecdb_rdatasetiter_t;
+
+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
+};
+
+isc_result_t
+dns_ecdb_register(isc_mem_t *mctx, dns_dbimplementation_t **dbimp) {
+ REQUIRE(mctx != NULL);
+ REQUIRE(dbimp != NULL && *dbimp == NULL);
+
+ return (dns_db_register("ecdb", dns_ecdb_create, NULL, mctx, dbimp));
+}
+
+void
+dns_ecdb_unregister(dns_dbimplementation_t **dbimp) {
+ REQUIRE(dbimp != NULL && *dbimp != NULL);
+
+ dns_db_unregister(dbimp);
+}
+
+/*%
+ * DB routines
+ */
+
+static void
+attach(dns_db_t *source, dns_db_t **targetp) {
+ dns_ecdb_t *ecdb = (dns_ecdb_t *)source;
+
+ REQUIRE(VALID_ECDB(ecdb));
+ REQUIRE(targetp != NULL && *targetp == NULL);
+
+ LOCK(&ecdb->lock);
+ ecdb->references++;
+ UNLOCK(&ecdb->lock);
+
+ *targetp = source;
+}
+
+static void
+destroy_ecdb(dns_ecdb_t **ecdbp) {
+ dns_ecdb_t *ecdb = *ecdbp;
+ isc_mem_t *mctx = ecdb->common.mctx;
+
+ if (dns_name_dynamic(&ecdb->common.origin))
+ dns_name_free(&ecdb->common.origin, mctx);
+
+ DESTROYLOCK(&ecdb->lock);
+
+ ecdb->common.impmagic = 0;
+ ecdb->common.magic = 0;
+
+ isc_mem_putanddetach(&mctx, ecdb, sizeof(*ecdb));
+
+ *ecdbp = NULL;
+}
+
+static void
+detach(dns_db_t **dbp) {
+ dns_ecdb_t *ecdb;
+ bool need_destroy = false;
+
+ REQUIRE(dbp != NULL);
+ ecdb = (dns_ecdb_t *)*dbp;
+ REQUIRE(VALID_ECDB(ecdb));
+
+ LOCK(&ecdb->lock);
+ ecdb->references--;
+ if (ecdb->references == 0 && ISC_LIST_EMPTY(ecdb->nodes))
+ need_destroy = true;
+ UNLOCK(&ecdb->lock);
+
+ if (need_destroy)
+ destroy_ecdb(&ecdb);
+
+ *dbp = NULL;
+}
+
+static void
+attachnode(dns_db_t *db, dns_dbnode_t *source, dns_dbnode_t **targetp) {
+ dns_ecdb_t *ecdb = (dns_ecdb_t *)db;
+ dns_ecdbnode_t *node = (dns_ecdbnode_t *)source;
+
+ REQUIRE(VALID_ECDB(ecdb));
+ REQUIRE(VALID_ECDBNODE(node));
+ REQUIRE(targetp != NULL && *targetp == NULL);
+
+ LOCK(&node->lock);
+ INSIST(node->references > 0);
+ node->references++;
+ INSIST(node->references != 0); /* Catch overflow. */
+ UNLOCK(&node->lock);
+
+ *targetp = node;
+}
+
+static void
+destroynode(dns_ecdbnode_t *node) {
+ isc_mem_t *mctx;
+ dns_ecdb_t *ecdb = node->ecdb;
+ bool need_destroydb = false;
+ rdatasetheader_t *header;
+
+ mctx = ecdb->common.mctx;
+
+ LOCK(&ecdb->lock);
+ ISC_LIST_UNLINK(ecdb->nodes, node, link);
+ if (ecdb->references == 0 && ISC_LIST_EMPTY(ecdb->nodes))
+ need_destroydb = true;
+ UNLOCK(&ecdb->lock);
+
+ dns_name_free(&node->name, mctx);
+
+ while ((header = ISC_LIST_HEAD(node->rdatasets)) != NULL) {
+ unsigned int headersize;
+
+ ISC_LIST_UNLINK(node->rdatasets, header, link);
+ headersize =
+ dns_rdataslab_size((unsigned char *)header,
+ sizeof(*header));
+ isc_mem_put(mctx, header, headersize);
+ }
+
+ DESTROYLOCK(&node->lock);
+
+ node->magic = 0;
+ isc_mem_put(mctx, node, sizeof(*node));
+
+ if (need_destroydb)
+ destroy_ecdb(&ecdb);
+}
+
+static void
+detachnode(dns_db_t *db, dns_dbnode_t **nodep) {
+ dns_ecdb_t *ecdb = (dns_ecdb_t *)db;
+ dns_ecdbnode_t *node;
+ bool need_destroy = false;
+
+ REQUIRE(VALID_ECDB(ecdb));
+ REQUIRE(nodep != NULL);
+ node = (dns_ecdbnode_t *)*nodep;
+ REQUIRE(VALID_ECDBNODE(node));
+
+ UNUSED(ecdb); /* in case REQUIRE() is empty */
+
+ LOCK(&node->lock);
+ INSIST(node->references > 0);
+ node->references--;
+ if (node->references == 0)
+ need_destroy = true;
+ UNLOCK(&node->lock);
+
+ if (need_destroy)
+ destroynode(node);
+
+ *nodep = NULL;
+}
+
+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_ecdb_t *ecdb = (dns_ecdb_t *)db;
+
+ REQUIRE(VALID_ECDB(ecdb));
+
+ UNUSED(name);
+ UNUSED(version);
+ UNUSED(type);
+ UNUSED(options);
+ UNUSED(now);
+ UNUSED(nodep);
+ UNUSED(foundname);
+ UNUSED(rdataset);
+ UNUSED(sigrdataset);
+
+ return (ISC_R_NOTFOUND);
+}
+
+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)
+{
+ dns_ecdb_t *ecdb = (dns_ecdb_t *)db;
+
+ REQUIRE(VALID_ECDB(ecdb));
+
+ UNUSED(name);
+ UNUSED(options);
+ UNUSED(now);
+ UNUSED(nodep);
+ UNUSED(foundname);
+ UNUSED(rdataset);
+ UNUSED(sigrdataset);
+
+ return (ISC_R_NOTFOUND);
+}
+
+static isc_result_t
+findnode(dns_db_t *db, dns_name_t *name, bool create,
+ dns_dbnode_t **nodep)
+{
+ dns_ecdb_t *ecdb = (dns_ecdb_t *)db;
+ isc_mem_t *mctx;
+ dns_ecdbnode_t *node;
+ isc_result_t result;
+
+ REQUIRE(VALID_ECDB(ecdb));
+ REQUIRE(nodep != NULL && *nodep == NULL);
+
+ UNUSED(name);
+
+ if (create != true) {
+ /* an 'ephemeral' node is never reused. */
+ return (ISC_R_NOTFOUND);
+ }
+
+ mctx = ecdb->common.mctx;
+ node = isc_mem_get(mctx, sizeof(*node));
+ if (node == NULL)
+ return (ISC_R_NOMEMORY);
+
+ 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(mctx, node, sizeof(*node));
+ return (ISC_R_UNEXPECTED);
+ }
+
+ dns_name_init(&node->name, NULL);
+ result = dns_name_dup(name, mctx, &node->name);
+ if (result != ISC_R_SUCCESS) {
+ DESTROYLOCK(&node->lock);
+ isc_mem_put(mctx, node, sizeof(*node));
+ return (result);
+ }
+ node->ecdb= ecdb;
+ node->references = 1;
+ ISC_LIST_INIT(node->rdatasets);
+
+ ISC_LINK_INIT(node, link);
+
+ LOCK(&ecdb->lock);
+ ISC_LIST_APPEND(ecdb->nodes, node, link);
+ UNLOCK(&ecdb->lock);
+
+ node->magic = ECDBNODE_MAGIC;
+
+ *nodep = node;
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+bind_rdataset(dns_ecdb_t *ecdb, dns_ecdbnode_t *node,
+ rdatasetheader_t *header, dns_rdataset_t *rdataset)
+{
+ unsigned char *raw;
+
+ /*
+ * Caller must be holding the node lock.
+ */
+
+ REQUIRE(!dns_rdataset_isassociated(rdataset));
+
+ rdataset->methods = &rdataset_methods;
+ rdataset->rdclass = ecdb->common.rdclass;
+ rdataset->type = header->type;
+ rdataset->covers = header->covers;
+ rdataset->ttl = header->ttl;
+ rdataset->trust = header->trust;
+ if (NXDOMAIN(header))
+ rdataset->attributes |= DNS_RDATASETATTR_NXDOMAIN;
+ if (NEGATIVE(header))
+ rdataset->attributes |= DNS_RDATASETATTR_NEGATIVE;
+
+ rdataset->private1 = ecdb;
+ rdataset->private2 = node;
+ raw = (unsigned char *)header + sizeof(*header);
+ rdataset->private3 = raw;
+ rdataset->count = 0;
+
+ /*
+ * Reset iterator state.
+ */
+ rdataset->privateuint4 = 0;
+ rdataset->private5 = NULL;
+
+ INSIST(node->references > 0);
+ node->references++;
+}
+
+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_ecdb_t *ecdb = (dns_ecdb_t *)db;
+ isc_region_t r;
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_mem_t *mctx;
+ dns_ecdbnode_t *ecdbnode = (dns_ecdbnode_t *)node;
+ rdatasetheader_t *header;
+
+ REQUIRE(VALID_ECDB(ecdb));
+ REQUIRE(VALID_ECDBNODE(ecdbnode));
+
+ UNUSED(version);
+ UNUSED(now);
+ UNUSED(options);
+
+ mctx = ecdb->common.mctx;
+
+ LOCK(&ecdbnode->lock);
+
+ /*
+ * Sanity check: this implementation does not allow overriding an
+ * existing rdataset of the same type.
+ */
+ for (header = ISC_LIST_HEAD(ecdbnode->rdatasets); header != NULL;
+ header = ISC_LIST_NEXT(header, link)) {
+ INSIST(header->type != rdataset->type ||
+ header->covers != rdataset->covers);
+ }
+
+ result = dns_rdataslab_fromrdataset(rdataset, mctx,
+ &r, sizeof(rdatasetheader_t));
+ if (result != ISC_R_SUCCESS)
+ goto unlock;
+
+ header = (rdatasetheader_t *)r.base;
+ header->type = rdataset->type;
+ header->ttl = rdataset->ttl;
+ header->trust = rdataset->trust;
+ header->covers = rdataset->covers;
+ header->attributes = 0;
+ if ((rdataset->attributes & DNS_RDATASETATTR_NXDOMAIN) != 0)
+ header->attributes |= RDATASET_ATTR_NXDOMAIN;
+ if ((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0)
+ header->attributes |= RDATASET_ATTR_NEGATIVE;
+ ISC_LINK_INIT(header, link);
+ ISC_LIST_APPEND(ecdbnode->rdatasets, header, link);
+
+ if (addedrdataset == NULL)
+ goto unlock;
+
+ bind_rdataset(ecdb, ecdbnode, header, addedrdataset);
+
+ unlock:
+ UNLOCK(&ecdbnode->lock);
+
+ 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)
+{
+ UNUSED(db);
+ UNUSED(node);
+ UNUSED(version);
+ UNUSED(type);
+ UNUSED(covers);
+
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static isc_result_t
+createiterator(dns_db_t *db, unsigned int options,
+ dns_dbiterator_t **iteratorp)
+{
+ UNUSED(db);
+ UNUSED(options);
+ UNUSED(iteratorp);
+
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+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_ecdb_t *ecdb = (dns_ecdb_t *)db;
+ dns_ecdbnode_t *ecdbnode = (dns_ecdbnode_t *)node;
+ isc_mem_t *mctx;
+ ecdb_rdatasetiter_t *iterator;
+
+ REQUIRE(VALID_ECDB(ecdb));
+ REQUIRE(VALID_ECDBNODE(ecdbnode));
+
+ mctx = ecdb->common.mctx;
+
+ iterator = isc_mem_get(mctx, sizeof(ecdb_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 dns_dbmethods_t ecdb_methods = {
+ attach,
+ detach,
+ NULL, /* beginload */
+ NULL, /* endload */
+ NULL, /* serialize */
+ NULL, /* dump */
+ NULL, /* currentversion */
+ NULL, /* newversion */
+ NULL, /* attachversion */
+ NULL, /* closeversion */
+ findnode,
+ find,
+ findzonecut,
+ attachnode,
+ detachnode,
+ NULL, /* expirenode */
+ NULL, /* printnode */
+ createiterator, /* createiterator */
+ NULL, /* findrdataset */
+ allrdatasets,
+ addrdataset,
+ NULL, /* subtractrdataset */
+ deleterdataset,
+ NULL, /* issecure */
+ NULL, /* nodecount */
+ NULL, /* ispersistent */
+ NULL, /* overmem */
+ NULL, /* settask */
+ NULL, /* getoriginnode */
+ NULL, /* transfernode */
+ NULL, /* getnsec3parameters */
+ NULL, /* findnsec3node */
+ NULL, /* setsigningtime */
+ NULL, /* getsigningtime */
+ NULL, /* resigned */
+ NULL, /* isdnssec */
+ NULL, /* getrrsetstats */
+ NULL, /* rpz_attach */
+ NULL, /* rpz_ready */
+ NULL, /* findnodeext */
+ NULL, /* findext */
+ NULL, /* setcachestats */
+ NULL, /* hashsize */
+ NULL, /* nodefullname */
+ NULL /* getsize */
+};
+
+static isc_result_t
+dns_ecdb_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_ecdb_t *ecdb;
+ isc_result_t result;
+
+ REQUIRE(mctx != NULL);
+ REQUIRE(origin == dns_rootname);
+ REQUIRE(type == dns_dbtype_cache);
+ REQUIRE(dbp != NULL && *dbp == NULL);
+
+ UNUSED(argc);
+ UNUSED(argv);
+ UNUSED(driverarg);
+
+ ecdb = isc_mem_get(mctx, sizeof(*ecdb));
+ if (ecdb == NULL)
+ return (ISC_R_NOMEMORY);
+
+ ecdb->common.attributes = DNS_DBATTR_CACHE;
+ ecdb->common.rdclass = rdclass;
+ ecdb->common.methods = &ecdb_methods;
+ dns_name_init(&ecdb->common.origin, NULL);
+ result = dns_name_dupwithoffsets(origin, mctx, &ecdb->common.origin);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(mctx, ecdb, sizeof(*ecdb));
+ return (result);
+ }
+
+ result = isc_mutex_init(&ecdb->lock);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "isc_mutex_init() failed: %s",
+ isc_result_totext(result));
+ if (dns_name_dynamic(&ecdb->common.origin))
+ dns_name_free(&ecdb->common.origin, mctx);
+ isc_mem_put(mctx, ecdb, sizeof(*ecdb));
+ return (ISC_R_UNEXPECTED);
+ }
+
+ ecdb->references = 1;
+ ISC_LIST_INIT(ecdb->nodes);
+
+ ecdb->common.mctx = NULL;
+ isc_mem_attach(mctx, &ecdb->common.mctx);
+ ecdb->common.impmagic = ECDB_MAGIC;
+ ecdb->common.magic = DNS_DB_MAGIC;
+
+ *dbp = (dns_db_t *)ecdb;
+
+ return (ISC_R_SUCCESS);
+}
+
+/*%
+ * Rdataset Methods
+ */
+
+static void
+rdataset_disassociate(dns_rdataset_t *rdataset) {
+ dns_db_t *db = rdataset->private1;
+ dns_dbnode_t *node = rdataset->private2;
+
+ dns_db_detachnode(db, &node);
+}
+
+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) {
+ 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;
+ unsigned int count;
+
+ count = raw[0] * 256 + raw[1];
+
+ return (count);
+}
+
+static void
+rdataset_settrust(dns_rdataset_t *rdataset, dns_trust_t trust) {
+ rdatasetheader_t *header = rdataset->private3;
+
+ header--;
+ header->trust = rdataset->trust = trust;
+}
+
+/*
+ * Rdataset Iterator Methods
+ */
+
+static void
+rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp) {
+ isc_mem_t *mctx;
+ union {
+ dns_rdatasetiter_t *rdatasetiterator;
+ ecdb_rdatasetiter_t *ecdbiterator;
+ } u;
+
+ REQUIRE(iteratorp != NULL);
+ REQUIRE(DNS_RDATASETITER_VALID(*iteratorp));
+
+ u.rdatasetiterator = *iteratorp;
+
+ mctx = u.ecdbiterator->common.db->mctx;
+ u.ecdbiterator->common.magic = 0;
+
+ dns_db_detachnode(u.ecdbiterator->common.db,
+ &u.ecdbiterator->common.node);
+ isc_mem_put(mctx, u.ecdbiterator,
+ sizeof(ecdb_rdatasetiter_t));
+
+ *iteratorp = NULL;
+}
+
+static isc_result_t
+rdatasetiter_first(dns_rdatasetiter_t *iterator) {
+ ecdb_rdatasetiter_t *ecdbiterator = (ecdb_rdatasetiter_t *)iterator;
+ dns_ecdbnode_t *ecdbnode = (dns_ecdbnode_t *)iterator->node;
+
+ REQUIRE(DNS_RDATASETITER_VALID(iterator));
+
+ if (ISC_LIST_EMPTY(ecdbnode->rdatasets))
+ return (ISC_R_NOMORE);
+ ecdbiterator->current = ISC_LIST_HEAD(ecdbnode->rdatasets);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+rdatasetiter_next(dns_rdatasetiter_t *iterator) {
+ ecdb_rdatasetiter_t *ecdbiterator = (ecdb_rdatasetiter_t *)iterator;
+
+ REQUIRE(DNS_RDATASETITER_VALID(iterator));
+
+ ecdbiterator->current = ISC_LIST_NEXT(ecdbiterator->current, link);
+ if (ecdbiterator->current == NULL)
+ return (ISC_R_NOMORE);
+ else
+ return (ISC_R_SUCCESS);
+}
+
+static void
+rdatasetiter_current(dns_rdatasetiter_t *iterator, dns_rdataset_t *rdataset) {
+ ecdb_rdatasetiter_t *ecdbiterator = (ecdb_rdatasetiter_t *)iterator;
+ dns_ecdb_t *ecdb;
+
+ ecdb = (dns_ecdb_t *)iterator->db;
+ REQUIRE(VALID_ECDB(ecdb));
+
+ bind_rdataset(ecdb, iterator->node, ecdbiterator->current, rdataset);
+}
diff --git a/lib/dns/fixedname.c b/lib/dns/fixedname.c
new file mode 100644
index 0000000..3d5de3b
--- /dev/null
+++ b/lib/dns/fixedname.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <dns/fixedname.h>
+
+void
+dns_fixedname_init(dns_fixedname_t *fixed) {
+ dns_name_init(&fixed->name, fixed->offsets);
+ isc_buffer_init(&fixed->buffer, fixed->data, DNS_NAME_MAXWIRE);
+ dns_name_setbuffer(&fixed->name, &fixed->buffer);
+}
+
+void
+dns_fixedname_invalidate(dns_fixedname_t *fixed) {
+ dns_name_invalidate(&fixed->name);
+}
+
+dns_name_t *
+dns_fixedname_name(dns_fixedname_t *fixed) {
+ return (&fixed->name);
+}
+
+dns_name_t *
+dns_fixedname_initname(dns_fixedname_t *fixed) {
+ dns_fixedname_init(fixed);
+ return (dns_fixedname_name(fixed));
+}
diff --git a/lib/dns/forward.c b/lib/dns/forward.c
new file mode 100644
index 0000000..cf1865f
--- /dev/null
+++ b/lib/dns/forward.c
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/rwlock.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_addfwd(dns_fwdtable_t *fwdtable, dns_name_t *name,
+ dns_forwarderlist_t *fwdrs, dns_fwdpolicy_t fwdpolicy)
+{
+ isc_result_t result;
+ dns_forwarders_t *forwarders;
+ dns_forwarder_t *fwd, *nfwd;
+
+ 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->fwdrs);
+ for (fwd = ISC_LIST_HEAD(*fwdrs);
+ fwd != NULL;
+ fwd = ISC_LIST_NEXT(fwd, link))
+ {
+ nfwd = isc_mem_get(fwdtable->mctx, sizeof(dns_forwarder_t));
+ if (nfwd == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ *nfwd = *fwd;
+ ISC_LINK_INIT(nfwd, link);
+ ISC_LIST_APPEND(forwarders->fwdrs, nfwd, 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->fwdrs)) {
+ fwd = ISC_LIST_HEAD(forwarders->fwdrs);
+ ISC_LIST_UNLINK(forwarders->fwdrs, fwd, link);
+ isc_mem_put(fwdtable->mctx, fwd, sizeof(isc_sockaddr_t));
+ }
+ isc_mem_put(fwdtable->mctx, forwarders, sizeof(dns_forwarders_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;
+ dns_forwarder_t *fwd;
+ isc_sockaddr_t *sa;
+
+ 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->fwdrs);
+ for (sa = ISC_LIST_HEAD(*addrs);
+ sa != NULL;
+ sa = ISC_LIST_NEXT(sa, link))
+ {
+ fwd = isc_mem_get(fwdtable->mctx, sizeof(dns_forwarder_t));
+ if (fwd == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ fwd->addr = *sa;
+ fwd->dscp = -1;
+ ISC_LINK_INIT(fwd, link);
+ ISC_LIST_APPEND(forwarders->fwdrs, fwd, 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->fwdrs)) {
+ fwd = ISC_LIST_HEAD(forwarders->fwdrs);
+ ISC_LIST_UNLINK(forwarders->fwdrs, fwd, link);
+ isc_mem_put(fwdtable->mctx, fwd, sizeof(dns_forwarder_t));
+ }
+ isc_mem_put(fwdtable->mctx, forwarders, sizeof(dns_forwarders_t));
+ return (result);
+}
+
+isc_result_t
+dns_fwdtable_delete(dns_fwdtable_t *fwdtable, dns_name_t *name) {
+ isc_result_t result;
+
+ REQUIRE(VALID_FWDTABLE(fwdtable));
+
+ RWLOCK(&fwdtable->rwlock, isc_rwlocktype_write);
+ result = dns_rbt_deletename(fwdtable->table, name, false);
+ RWUNLOCK(&fwdtable->rwlock, isc_rwlocktype_write);
+
+ if (result == DNS_R_PARTIALMATCH)
+ result = ISC_R_NOTFOUND;
+
+ 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;
+ dns_forwarder_t *fwd;
+
+ UNUSED(arg);
+
+ while (!ISC_LIST_EMPTY(forwarders->fwdrs)) {
+ fwd = ISC_LIST_HEAD(forwarders->fwdrs);
+ ISC_LIST_UNLINK(forwarders->fwdrs, fwd, link);
+ isc_mem_put(fwdtable->mctx, fwd, sizeof(dns_forwarder_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..84c3e7f
--- /dev/null
+++ b/lib/dns/gen-unix.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \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 declared 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 <stdbool.h>
+#include <unistd.h> /* XXXDCL Required for ?. */
+
+#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 bool
+start_directory(const char *path, isc_dir_t *dir) {
+ dir->handle = opendir(path);
+
+ if (dir->handle != NULL)
+ return (true);
+ else
+ return (false);
+
+}
+
+static bool
+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 (true);
+ else
+ return (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..4791032
--- /dev/null
+++ b/lib/dns/gen-win32.h
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * 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. 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.
+ */
+
+/*
+ * \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 declared 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 <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <windows.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. */
+
+bool isc_commandline_errprint = true; /* Print error messages. */
+bool isc_commandline_reset = 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 = 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;
+ bool first_file;
+ char *filename;
+} isc_dir_t;
+
+bool
+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 (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 = true;
+
+ dir->handle = FindFirstFile(pattern, &dir->find_data);
+
+ if (dir->handle == INVALID_HANDLE_VALUE) {
+ dir->filename = NULL;
+ return (false);
+ } else {
+ dir->filename = dir->find_data.cFileName;
+ return (true);
+ }
+}
+
+bool
+next_file(isc_dir_t *dir) {
+ if (dir->first_file)
+ dir->first_file = 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 (true);
+ else
+ return (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..2a3b94b
--- /dev/null
+++ b/lib/dns/gen.c
@@ -0,0 +1,923 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#ifdef WIN32
+/*
+ * Silence compiler warnings about using strcpy and friends.
+ */
+#define _CRT_SECURE_NO_DEPRECATE 1
+/*
+ * We use snprintf which was defined late in Windows even it is in C99.
+ */
+#if _MSC_VER < 1900
+#define snprintf _snprintf
+#endif
+#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 INSIST(cond) \
+ if (!(cond)) { \
+ fprintf(stderr, "%s:%d: INSIST(%s)\n", \
+ __FILE__, __LINE__, #cond); \
+ abort(); \
+ }
+
+#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 = true"
+
+#define FROMWIREARGS "rdclass, type, source, dctx, options, target"
+#define FROMWIRECLASS "rdclass"
+#define FROMWIRETYPE "type"
+#define FROMWIREDEF "use_default = true"
+
+#define TOWIREARGS "rdata, cctx, target"
+#define TOWIRECLASS "rdata->rdclass"
+#define TOWIRETYPE "rdata->type"
+#define TOWIREDEF "use_default = true"
+
+#define FROMSTRUCTARGS "rdclass, type, source, target"
+#define FROMSTRUCTCLASS "rdclass"
+#define FROMSTRUCTTYPE "type"
+#define FROMSTRUCTDEF "use_default = true"
+
+#define TOSTRUCTARGS "rdata, target, mctx"
+#define TOSTRUCTCLASS "rdata->rdclass"
+#define TOSTRUCTTYPE "rdata->type"
+#define TOSTRUCTDEF "use_default = 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 = true"
+
+#define ADDITIONALDATAARGS "rdata, add, arg"
+#define ADDITIONALDATACLASS "rdata->rdclass"
+#define ADDITIONALDATATYPE "rdata->type"
+#define ADDITIONALDATADEF "use_default = true"
+
+#define DIGESTARGS "rdata, digest, arg"
+#define DIGESTCLASS "rdata->rdclass"
+#define DIGESTTYPE "rdata->type"
+#define DIGESTDEF "use_default = true"
+
+#define CHECKOWNERARGS "name, rdclass, type, wildcard"
+#define CHECKOWNERCLASS "rdclass"
+#define CHECKOWNERTYPE "type"
+#define CHECKOWNERDEF "result = true"
+
+#define CHECKNAMESARGS "rdata, owner, bad"
+#define CHECKNAMESCLASS "rdata->rdclass"
+#define CHECKNAMESTYPE "rdata->type"
+#define CHECKNAMESDEF "result = true"
+
+static const char copyright[] =
+"/*\n"
+" * Copyright (C) 1998%s Internet Systems Consortium, Inc. (\"ISC\")\n"
+" *\n"
+" * This Source Code Form is subject to the terms of the Mozilla Public\n"
+" * License, v. 2.0. If a copy of the MPL was not distributed with this\n"
+" * file, You can obtain one at http://mozilla.org/MPL/2.0/.\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 STR_EXPAND(tok) #tok
+#define STR(tok) STR_EXPAND(tok)
+
+#define TYPENAMES 256
+#define TYPECLASSLEN 20 /* DNS mnemonic size. Must be less than 100. */
+#define TYPECLASSBUF (TYPECLASSLEN + 1)
+#define TYPECLASSFMT "%" STR(TYPECLASSLEN) "[-0-9a-z]_%d"
+#define ATTRIBUTESIZE 256
+#define DIRNAMESIZE 256
+
+static struct cc {
+ struct cc *next;
+ int rdclass;
+ char classname[TYPECLASSBUF];
+} *classes;
+
+static struct tt {
+ struct tt *next;
+ int rdclass;
+ int type;
+ char classname[TYPECLASSBUF];
+ char typename[TYPECLASSBUF];
+ char dirname[DIRNAMESIZE]; /* XXX Should be max path length */
+} *types;
+
+static struct ttnam {
+ char typename[TYPECLASSBUF];
+ char macroname[TYPECLASSBUF];
+ char attr[ATTRIBUTESIZE];
+ unsigned int sorted;
+ int type;
+} typenames[TYPENAMES];
+
+static int maxtype = -1;
+
+static char *
+upper(char *);
+static char *
+funname(const char *, char *);
+static void
+doswitch(const char *, const char *, const char *, const char *,
+ const char *, const char *);
+static void
+add(int, const char *, int, const char *, const char *);
+static void
+sd(int, const char *, const char *, char);
+static 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.
+ */
+static 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]);
+}
+
+static char *
+funname(const char *s, char *buf) {
+ char *b = buf;
+ char c;
+
+ INSIST(strlen(s) < TYPECLASSBUF);
+ while ((c = *s++)) {
+ *b++ = (c == '-') ? '_' : c;
+ }
+ *b = '\0';
+ return (buf);
+}
+
+static 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[TYPECLASSBUF], buf2[TYPECLASSBUF];
+ 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);
+ }
+}
+
+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);
+}
+
+static void
+insert_into_typenames(int type, const char *typename, const char *attr) {
+ struct ttnam *ttn = NULL;
+ size_t c;
+ int i, n;
+ char tmp[256];
+
+ INSIST(strlen(typename) < TYPECLASSBUF);
+ 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);
+ }
+
+ /* XXXMUKS: This is redundant due to the INSIST above. */
+ if (strlen(typename) > sizeof(ttn->typename) - 1) {
+ fprintf(stderr, "Error: type name %s is too long\n",
+ typename);
+ exit(1);
+ }
+
+ strncpy(ttn->typename, typename, sizeof(ttn->typename));
+ ttn->typename[sizeof(ttn->typename) - 1] = '\0';
+
+ strncpy(ttn->macroname, ttn->typename, sizeof(ttn->macroname));
+ ttn->macroname[sizeof(ttn->macroname) - 1] = '\0';
+
+ ttn->type = type;
+ c = strlen(ttn->macroname);
+ while (c > 0) {
+ if (ttn->macroname[c - 1] == '-')
+ ttn->macroname[c - 1] = '_';
+ c--;
+ }
+
+ if (attr == NULL) {
+ n = snprintf(tmp, sizeof(tmp),
+ "RRTYPE_%s_ATTRIBUTES", upper(ttn->macroname));
+ INSIST(n > 0 && (unsigned)n < sizeof(tmp));
+ 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);
+ }
+
+ strncpy(ttn->attr, attr, sizeof(ttn->attr));
+ ttn->attr[sizeof(ttn->attr) - 1] = '\0';
+
+ ttn->sorted = 0;
+ if (maxtype < type)
+ maxtype = type;
+}
+
+static 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;
+
+ INSIST(strlen(typename) < TYPECLASSBUF);
+ INSIST(strlen(classname) < TYPECLASSBUF);
+ INSIST(strlen(dirname) < DIRNAMESIZE);
+
+ 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;
+
+ strncpy(newtt->classname, classname, sizeof(newtt->classname));
+ newtt->classname[sizeof(newtt->classname) - 1] = '\0';
+
+ strncpy(newtt->typename, typename, sizeof(newtt->typename));
+ newtt->typename[sizeof(newtt->typename) - 1] = '\0';
+
+ if (strncmp(dirname, "./", 2) == 0)
+ dirname += 2;
+ strncpy(newtt->dirname, dirname, sizeof(newtt->dirname));
+ newtt->dirname[sizeof(newtt->dirname) - 1] = '\0';
+
+ 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));
+ if (newcc == NULL) {
+ fprintf(stderr, "malloc() failed\n");
+ exit(1);
+ }
+ newcc->rdclass = rdclass;
+ strncpy(newcc->classname, classname, sizeof(newcc->classname));
+ newcc->classname[sizeof(newcc->classname) - 1] = '\0';
+ 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;
+}
+
+static void
+sd(int rdclass, const char *classname, const char *dirname, char filetype) {
+ char buf[TYPECLASSLEN + sizeof("_65535.h")];
+ char typename[TYPECLASSBUF];
+ int type, n;
+ isc_dir_t dir;
+
+ if (!start_directory(dirname, &dir))
+ return;
+
+ while (next_file(&dir)) {
+ if (sscanf(dir.filename, TYPECLASSFMT, typename, &type) != 2)
+ continue;
+ if ((type > 65535) || (type < 0))
+ continue;
+
+ n = snprintf(buf, sizeof(buf), "%s_%d.%c", typename,
+ type, filetype);
+ INSIST(n > 0 && (unsigned)n < sizeof(buf));
+ if (strcmp(buf, dir.filename) != 0)
+ continue;
+ add(rdclass, classname, type, typename, dirname);
+ }
+
+ end_directory(&dir);
+}
+
+static unsigned int
+HASH(char *string) {
+ size_t 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[DIRNAMESIZE]; /* XXX Should be max path length */
+ char srcdir[DIRNAMESIZE]; /* XXX Should be max path length */
+ int rdclass;
+ char classname[TYPECLASSBUF];
+ 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, n;
+ char buf1[TYPECLASSBUF];
+ 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]));
+
+ srcdir[0] = '\0';
+ 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':
+ if (strlen(isc_commandline_argument) >
+ DIRNAMESIZE - 2 * TYPECLASSLEN -
+ sizeof("/rdata/_65535_65535")) {
+ fprintf(stderr, "\"%s\" too long\n",
+ isc_commandline_argument);
+ exit(1);
+ }
+ n = snprintf(srcdir, sizeof(srcdir), "%s/",
+ isc_commandline_argument);
+ INSIST(n > 0 && (unsigned)n < sizeof(srcdir));
+ 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);
+ }
+
+ n = snprintf(buf, sizeof(buf), "%srdata", srcdir);
+ INSIST(n > 0 && (unsigned)n < sizeof(srcdir));
+
+ if (!start_directory(buf, &dir))
+ exit(1);
+
+ while (next_file(&dir)) {
+ if (sscanf(dir.filename, TYPECLASSFMT, classname,
+ &rdclass) != 2)
+ continue;
+ if ((rdclass > 65535) || (rdclass < 0))
+ continue;
+
+ n = snprintf(buf, sizeof(buf), "%srdata/%s_%d",
+ srcdir, classname, rdclass);
+ INSIST(n > 0 && (unsigned)n < sizeof(buf));
+ if (strcmp(buf + 6 + strlen(srcdir), dir.filename) != 0)
+ continue;
+ sd(rdclass, classname, buf, filetype);
+ }
+ end_directory(&dir);
+ n = snprintf(buf, sizeof(buf), "%srdata/generic", srcdir);
+ INSIST(n > 0 && (unsigned)n < sizeof(srcdir));
+ sd(0, "", buf, filetype);
+
+ if (time(&now) != -1) {
+ if ((tm = localtime(&now)) != NULL && tm->tm_year > 104) {
+ n = snprintf(year, sizeof(year), "-%d",
+ tm->tm_year + 1900);
+ INSIST(n > 0 && (unsigned)n < sizeof(year));
+ } else {
+ snprintf(year, sizeof(year), "-2016");
+ }
+ } else {
+ snprintf(year, sizeof(year), "-2016");
+ }
+
+ 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/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("CASECOMPARESWITCH", "casecompare", 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\", %d, "
+ "_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 %d: 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;
+ /*
+ * Remove KEYDATA (65533) from the type to memonic
+ * translation as it is internal use only. This
+ * stops the tools from displaying KEYDATA instead
+ * of TYPE65533.
+ */
+ if (i == 65533U)
+ continue;
+ fprintf(stdout, "\tcase %d: 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) {
+ snprintf(buf, sizeof(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/geoip.c b/lib/dns/geoip.c
new file mode 100644
index 0000000..ec20baa
--- /dev/null
+++ b/lib/dns/geoip.c
@@ -0,0 +1,881 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdbool.h>
+
+#include <isc/util.h>
+
+#include <isc/mem.h>
+#include <isc/once.h>
+#include <isc/string.h>
+
+#include <dns/acl.h>
+#include <dns/geoip.h>
+
+#include <isc/thread.h>
+#include <math.h>
+#ifndef WIN32
+#include <netinet/in.h>
+#else
+#ifndef _WINSOCKAPI_
+#define _WINSOCKAPI_ /* Prevent inclusion of winsock.h in windows.h */
+#endif
+#include <winsock2.h>
+#endif /* WIN32 */
+#include <dns/log.h>
+
+#ifdef HAVE_GEOIP
+#include <GeoIP.h>
+#include <GeoIPCity.h>
+
+/*
+ * This structure preserves state from the previous GeoIP lookup,
+ * so that successive lookups for the same data from the same IP
+ * address will not require repeated calls into the GeoIP library
+ * to look up data in the database. This should improve performance
+ * somewhat.
+ *
+ * For lookups in the City and Region databases, we preserve pointers
+ * to the GeoIPRecord and GeoIPregion structures; these will need to be
+ * freed by GeoIPRecord_delete() and GeoIPRegion_delete().
+ *
+ * for lookups in ISP, AS, Org and Domain we prserve a pointer to
+ * the returned name; these must be freed by free().
+ *
+ * For lookups in Country we preserve a pointer to the text of
+ * the country code, name, etc (we use a different pointer for this
+ * than for the names returned by Org, ISP, etc, because those need
+ * to be freed but country lookups do not).
+ *
+ * For lookups in Netspeed we preserve the returned ID.
+ *
+ * XXX: Currently this mechanism is only used for IPv4 lookups; the
+ * family and addr6 fields are to be used IPv6 is added.
+ */
+typedef struct geoip_state {
+ uint16_t subtype;
+ unsigned int family;
+ uint32_t ipnum;
+ geoipv6_t ipnum6;
+ uint8_t scope;
+ GeoIPRecord *record;
+ GeoIPRegion *region;
+ const char *text;
+ char *name;
+ int id;
+ isc_mem_t *mctx;
+} geoip_state_t;
+
+#ifdef ISC_PLATFORM_USETHREADS
+static isc_mutex_t key_mutex;
+static bool state_key_initialized = false;
+static isc_thread_key_t state_key;
+static isc_once_t mutex_once = ISC_ONCE_INIT;
+static isc_mem_t *state_mctx = NULL;
+
+static void
+key_mutex_init(void) {
+ RUNTIME_CHECK(isc_mutex_init(&key_mutex) == ISC_R_SUCCESS);
+}
+
+static void
+free_state(void *arg) {
+ geoip_state_t *state = arg;
+ if (state != NULL && state->record != NULL)
+ GeoIPRecord_delete(state->record);
+ if (state != NULL)
+ isc_mem_putanddetach(&state->mctx,
+ state, sizeof(geoip_state_t));
+ isc_thread_key_setspecific(state_key, NULL);
+}
+
+static isc_result_t
+state_key_init(void) {
+ isc_result_t result;
+
+ result = isc_once_do(&mutex_once, key_mutex_init);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ if (!state_key_initialized) {
+ LOCK(&key_mutex);
+ if (!state_key_initialized) {
+ int ret;
+
+ if (state_mctx == NULL)
+ result = isc_mem_create2(0, 0, &state_mctx, 0);
+ if (result != ISC_R_SUCCESS)
+ goto unlock;
+ isc_mem_setname(state_mctx, "geoip_state", NULL);
+ isc_mem_setdestroycheck(state_mctx, false);
+
+ ret = isc_thread_key_create(&state_key, free_state);
+ if (ret == 0)
+ state_key_initialized = true;
+ else
+ result = ISC_R_FAILURE;
+ }
+ unlock:
+ UNLOCK(&key_mutex);
+ }
+
+ return (result);
+}
+#else
+static geoip_state_t saved_state;
+#endif
+
+static void
+clean_state(geoip_state_t *state) {
+ if (state == NULL)
+ return;
+
+ if (state->record != NULL) {
+ GeoIPRecord_delete(state->record);
+ state->record = NULL;
+ }
+ if (state->region != NULL) {
+ GeoIPRegion_delete(state->region);
+ state->region = NULL;
+ }
+ if (state->name != NULL) {
+ free (state->name);
+ state->name = NULL;
+ }
+ state->ipnum = 0;
+ state->text = NULL;
+ state->id = 0;
+}
+
+static isc_result_t
+set_state(unsigned int family, uint32_t ipnum, const geoipv6_t *ipnum6,
+ uint8_t scope, dns_geoip_subtype_t subtype, GeoIPRecord *record,
+ GeoIPRegion *region, char *name, const char *text, int id)
+{
+ geoip_state_t *state = NULL;
+#ifdef ISC_PLATFORM_USETHREADS
+ isc_result_t result;
+
+ result = state_key_init();
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ state = (geoip_state_t *) isc_thread_key_getspecific(state_key);
+ if (state == NULL) {
+ state = (geoip_state_t *) isc_mem_get(state_mctx,
+ sizeof(geoip_state_t));
+ if (state == NULL)
+ return (ISC_R_NOMEMORY);
+ memset(state, 0, sizeof(*state));
+
+ result = isc_thread_key_setspecific(state_key, state);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(state_mctx, state, sizeof(geoip_state_t));
+ return (result);
+ }
+
+ isc_mem_attach(state_mctx, &state->mctx);
+ } else
+ clean_state(state);
+#else
+ state = &saved_state;
+ clean_state(state);
+#endif
+
+ if (family == AF_INET) {
+ state->ipnum = ipnum;
+ } else {
+ INSIST(ipnum6 != NULL);
+ state->ipnum6 = *ipnum6;
+ }
+
+ state->family = family;
+ state->subtype = subtype;
+ state->scope = scope;
+ state->record = record;
+ state->region = region;
+ state->name = name;
+ state->text = text;
+ state->id = id;
+
+ return (ISC_R_SUCCESS);
+}
+
+static geoip_state_t *
+get_state_for(unsigned int family, uint32_t ipnum,
+ const geoipv6_t *ipnum6)
+{
+ geoip_state_t *state;
+
+#ifdef ISC_PLATFORM_USETHREADS
+ isc_result_t result;
+
+ result = state_key_init();
+ if (result != ISC_R_SUCCESS)
+ return (NULL);
+
+ state = (geoip_state_t *) isc_thread_key_getspecific(state_key);
+ if (state == NULL)
+ return (NULL);
+#else
+ state = &saved_state;
+#endif
+
+ if (state->family == family &&
+ ((state->family == AF_INET && state->ipnum == ipnum) ||
+ (state->family == AF_INET6 && ipnum6 != NULL &&
+ memcmp(state->ipnum6.s6_addr, ipnum6->s6_addr, 16) == 0)))
+ return (state);
+
+ return (NULL);
+}
+
+/*
+ * Country lookups are performed if the previous lookup was from a
+ * different IP address than the current, or was for a search of a
+ * different subtype.
+ */
+static const char *
+country_lookup(GeoIP *db, dns_geoip_subtype_t subtype,
+ unsigned int family,
+ uint32_t ipnum, const geoipv6_t *ipnum6,
+ uint8_t *scope)
+{
+ geoip_state_t *prev_state = NULL;
+ const char *text = NULL;
+ GeoIPLookup gl;
+
+ REQUIRE(db != NULL);
+
+#ifndef HAVE_GEOIP_V6
+ /* no IPv6 support? give up now */
+ if (family == AF_INET6)
+ return (NULL);
+#endif
+
+ prev_state = get_state_for(family, ipnum, ipnum6);
+ if (prev_state != NULL && prev_state->subtype == subtype) {
+ text = prev_state->text;
+ if (scope != NULL)
+ *scope = prev_state->scope;
+ }
+
+ if (text == NULL) {
+ switch (subtype) {
+ case dns_geoip_country_code:
+ if (family == AF_INET)
+ text = GeoIP_country_code_by_ipnum_gl(db,
+ ipnum, &gl);
+#ifdef HAVE_GEOIP_V6
+ else
+ text = GeoIP_country_code_by_ipnum_v6_gl(db,
+ *ipnum6, &gl);
+#endif
+ break;
+ case dns_geoip_country_code3:
+ if (family == AF_INET)
+ text = GeoIP_country_code3_by_ipnum_gl(db,
+ ipnum, &gl);
+#ifdef HAVE_GEOIP_V6
+ else
+ text = GeoIP_country_code3_by_ipnum_v6_gl(db,
+ *ipnum6, &gl);
+#endif
+ break;
+ case dns_geoip_country_name:
+ if (family == AF_INET)
+ text = GeoIP_country_name_by_ipnum_gl(db,
+ ipnum, &gl);
+#ifdef HAVE_GEOIP_V6
+ else
+ text = GeoIP_country_name_by_ipnum_v6_gl(db,
+ *ipnum6, &gl);
+#endif
+ break;
+ default:
+ INSIST(0);
+ }
+
+ if (text == NULL)
+ return (NULL);
+
+ if (scope != NULL)
+ *scope = gl.netmask;
+
+ set_state(family, ipnum, ipnum6, gl.netmask, subtype,
+ NULL, NULL, NULL, text, 0);
+ }
+
+ return (text);
+}
+
+static char *
+city_string(GeoIPRecord *record, dns_geoip_subtype_t subtype, int *maxlen) {
+ const char *s;
+ char *deconst;
+
+ REQUIRE(record != NULL);
+ REQUIRE(maxlen != NULL);
+
+ /* Set '*maxlen' to the maximum length of this subtype, if any */
+ switch (subtype) {
+ case dns_geoip_city_countrycode:
+ case dns_geoip_city_region:
+ case dns_geoip_city_continentcode:
+ *maxlen = 2;
+ break;
+
+ case dns_geoip_city_countrycode3:
+ *maxlen = 3;
+ break;
+
+ default:
+ /* No fixed length; just use strcasecmp() for comparison */
+ *maxlen = 255;
+ }
+
+ switch (subtype) {
+ case dns_geoip_city_countrycode:
+ return (record->country_code);
+ case dns_geoip_city_countrycode3:
+ return (record->country_code3);
+ case dns_geoip_city_countryname:
+ return (record->country_name);
+ case dns_geoip_city_region:
+ return (record->region);
+ case dns_geoip_city_regionname:
+ s = GeoIP_region_name_by_code(record->country_code,
+ record->region);
+ DE_CONST(s, deconst);
+ return (deconst);
+ case dns_geoip_city_name:
+ return (record->city);
+ case dns_geoip_city_postalcode:
+ return (record->postal_code);
+ case dns_geoip_city_continentcode:
+ return (record->continent_code);
+ case dns_geoip_city_timezonecode:
+ s = GeoIP_time_zone_by_country_and_region(record->country_code,
+ record->region);
+ DE_CONST(s, deconst);
+ return (deconst);
+ default:
+ INSIST(0);
+ }
+}
+
+static bool
+is_city(dns_geoip_subtype_t subtype) {
+ switch (subtype) {
+ case dns_geoip_city_countrycode:
+ case dns_geoip_city_countrycode3:
+ case dns_geoip_city_countryname:
+ case dns_geoip_city_region:
+ case dns_geoip_city_regionname:
+ case dns_geoip_city_name:
+ case dns_geoip_city_postalcode:
+ case dns_geoip_city_continentcode:
+ case dns_geoip_city_timezonecode:
+ case dns_geoip_city_metrocode:
+ case dns_geoip_city_areacode:
+ return (true);
+ default:
+ return (false);
+ }
+}
+
+/*
+ * GeoIPRecord lookups are performed if the previous lookup was
+ * from a different IP address than the current, or was for a search
+ * outside the City database.
+ */
+static GeoIPRecord *
+city_lookup(GeoIP *db, dns_geoip_subtype_t subtype,
+ unsigned int family, uint32_t ipnum,
+ const geoipv6_t *ipnum6,
+ uint8_t *scope)
+{
+ GeoIPRecord *record = NULL;
+ geoip_state_t *prev_state = NULL;
+
+ REQUIRE(db != NULL);
+
+#ifndef HAVE_GEOIP_V6
+ /* no IPv6 support? give up now */
+ if (family == AF_INET6)
+ return (NULL);
+#endif
+
+ prev_state = get_state_for(family, ipnum, ipnum6);
+ if (prev_state != NULL && is_city(prev_state->subtype)) {
+ record = prev_state->record;
+ if (scope != NULL)
+ *scope = record->netmask;
+ }
+
+ if (record == NULL) {
+ if (family == AF_INET)
+ record = GeoIP_record_by_ipnum(db, ipnum);
+#ifdef HAVE_GEOIP_V6
+ else
+ record = GeoIP_record_by_ipnum_v6(db, *ipnum6);
+#endif
+ if (record == NULL)
+ return (NULL);
+
+ if (scope != NULL)
+ *scope = record->netmask;
+
+ set_state(family, ipnum, ipnum6, record->netmask, subtype,
+ record, NULL, NULL, NULL, 0);
+ }
+
+ return (record);
+}
+
+static char * region_string(GeoIPRegion *region, dns_geoip_subtype_t subtype, int *maxlen) {
+ const char *s;
+ char *deconst;
+
+ REQUIRE(region != NULL);
+ REQUIRE(maxlen != NULL);
+
+ switch (subtype) {
+ case dns_geoip_region_countrycode:
+ *maxlen = 2;
+ return (region->country_code);
+ case dns_geoip_region_code:
+ *maxlen = 2;
+ return (region->region);
+ case dns_geoip_region_name:
+ *maxlen = 255;
+ s = GeoIP_region_name_by_code(region->country_code,
+ region->region);
+ DE_CONST(s, deconst);
+ return (deconst);
+ default:
+ INSIST(0);
+ }
+}
+
+static bool
+is_region(dns_geoip_subtype_t subtype) {
+ switch (subtype) {
+ case dns_geoip_region_countrycode:
+ case dns_geoip_region_code:
+ return (true);
+ default:
+ return (false);
+ }
+}
+
+/*
+ * GeoIPRegion lookups are performed if the previous lookup was
+ * from a different IP address than the current, or was for a search
+ * outside the Region database.
+ */
+static GeoIPRegion *
+region_lookup(GeoIP *db, dns_geoip_subtype_t subtype,
+ uint32_t ipnum, uint8_t *scope)
+{
+ GeoIPRegion *region = NULL;
+ geoip_state_t *prev_state = NULL;
+ GeoIPLookup gl;
+
+ REQUIRE(db != NULL);
+
+ prev_state = get_state_for(AF_INET, ipnum, NULL);
+ if (prev_state != NULL && is_region(prev_state->subtype)) {
+ region = prev_state->region;
+ if (scope != NULL)
+ *scope = prev_state->scope;
+ }
+
+ if (region == NULL) {
+ region = GeoIP_region_by_ipnum_gl(db, ipnum, &gl);
+ if (region == NULL)
+ return (NULL);
+
+ if (scope != NULL)
+ *scope = gl.netmask;
+
+ set_state(AF_INET, ipnum, NULL, gl.netmask,
+ subtype, NULL, region, NULL, NULL, 0);
+ }
+
+ return (region);
+}
+
+/*
+ * ISP, Organization, AS Number and Domain lookups are performed if
+ * the previous lookup was from a different IP address than the current,
+ * or was for a search of a different subtype.
+ */
+static char *
+name_lookup(GeoIP *db, dns_geoip_subtype_t subtype,
+ uint32_t ipnum, uint8_t *scope)
+{
+ char *name = NULL;
+ geoip_state_t *prev_state = NULL;
+ GeoIPLookup gl;
+
+ REQUIRE(db != NULL);
+
+ prev_state = get_state_for(AF_INET, ipnum, NULL);
+ if (prev_state != NULL && prev_state->subtype == subtype) {
+ name = prev_state->name;
+ if (scope != NULL)
+ *scope = prev_state->scope;
+ }
+
+ if (name == NULL) {
+ name = GeoIP_name_by_ipnum_gl(db, ipnum, &gl);
+ if (name == NULL)
+ return (NULL);
+
+ if (scope != NULL)
+ *scope = gl.netmask;
+
+ set_state(AF_INET, ipnum, NULL, gl.netmask,
+ subtype, NULL, NULL, name, NULL, 0);
+ }
+
+ return (name);
+}
+
+/*
+ * Netspeed lookups are performed if the previous lookup was from a
+ * different IP address than the current, or was for a search of a
+ * different subtype.
+ */
+static int
+netspeed_lookup(GeoIP *db, dns_geoip_subtype_t subtype,
+ uint32_t ipnum, uint8_t *scope)
+{
+ geoip_state_t *prev_state = NULL;
+ bool found = false;
+ GeoIPLookup gl;
+ int id = -1;
+
+ REQUIRE(db != NULL);
+
+ prev_state = get_state_for(AF_INET, ipnum, NULL);
+ if (prev_state != NULL && prev_state->subtype == subtype) {
+ id = prev_state->id;
+ if (scope != NULL)
+ *scope = prev_state->scope;
+ found = true;
+ }
+
+ if (!found) {
+ id = GeoIP_id_by_ipnum_gl(db, ipnum, &gl);
+ if (id == 0)
+ return (0);
+
+ if (scope != NULL)
+ *scope = gl.netmask;
+
+ set_state(AF_INET, ipnum, NULL, gl.netmask,
+ subtype, NULL, NULL, NULL, NULL, id);
+ }
+
+ return (id);
+}
+#endif /* HAVE_GEOIP */
+
+#define DB46(addr, geoip, name) \
+ ((addr->family == AF_INET) ? (geoip->name##_v4) : (geoip->name##_v6))
+
+#ifdef HAVE_GEOIP
+/*
+ * Find the best database to answer a generic subtype
+ */
+static dns_geoip_subtype_t
+fix_subtype(const isc_netaddr_t *reqaddr, const dns_geoip_databases_t *geoip,
+ dns_geoip_subtype_t subtype)
+{
+ dns_geoip_subtype_t ret = subtype;
+
+ switch (subtype) {
+ case dns_geoip_countrycode:
+ if (DB46(reqaddr, geoip, city) != NULL)
+ ret = dns_geoip_city_countrycode;
+ else if (reqaddr->family == AF_INET && geoip->region != NULL)
+ ret = dns_geoip_region_countrycode;
+ else if (DB46(reqaddr, geoip, country) != NULL)
+ ret = dns_geoip_country_code;
+ break;
+ case dns_geoip_countrycode3:
+ if (DB46(reqaddr, geoip, city) != NULL)
+ ret = dns_geoip_city_countrycode3;
+ else if (DB46(reqaddr, geoip, country) != NULL)
+ ret = dns_geoip_country_code3;
+ break;
+ case dns_geoip_countryname:
+ if (DB46(reqaddr, geoip, city) != NULL)
+ ret = dns_geoip_city_countryname;
+ else if (DB46(reqaddr, geoip, country) != NULL)
+ ret = dns_geoip_country_name;
+ break;
+ case dns_geoip_region:
+ if (DB46(reqaddr, geoip, city) != NULL)
+ ret = dns_geoip_city_region;
+ else if (reqaddr->family == AF_INET && geoip->region != NULL)
+ ret = dns_geoip_region_code;
+ break;
+ case dns_geoip_regionname:
+ if (DB46(reqaddr, geoip, city) != NULL)
+ ret = dns_geoip_city_regionname;
+ else if (reqaddr->family == AF_INET && geoip->region != NULL)
+ ret = dns_geoip_region_name;
+ break;
+ default:
+ break;
+ }
+
+ return (ret);
+}
+#endif /* HAVE_GEOIP */
+
+bool
+dns_geoip_match(const isc_netaddr_t *reqaddr, uint8_t *scope,
+ const dns_geoip_databases_t *geoip,
+ const dns_geoip_elem_t *elt)
+{
+#ifndef HAVE_GEOIP
+ UNUSED(reqaddr);
+ UNUSED(geoip);
+ UNUSED(elt);
+
+ return (false);
+#else
+ GeoIP *db;
+ GeoIPRecord *record;
+ GeoIPRegion *region;
+ dns_geoip_subtype_t subtype;
+ uint32_t ipnum = 0;
+ int maxlen = 0, id, family;
+ const char *cs;
+ char *s;
+#ifdef HAVE_GEOIP_V6
+ const geoipv6_t *ipnum6 = NULL;
+#else
+ const void *ipnum6 = NULL;
+#endif
+
+ INSIST(geoip != NULL);
+
+ family = reqaddr->family;
+ switch (family) {
+ case AF_INET:
+ ipnum = ntohl(reqaddr->type.in.s_addr);
+ break;
+ case AF_INET6:
+#ifdef HAVE_GEOIP_V6
+ ipnum6 = &reqaddr->type.in6;
+ break;
+#else
+ return (false);
+#endif
+ default:
+ return (false);
+ }
+
+ subtype = fix_subtype(reqaddr, geoip, elt->subtype);
+
+ switch (subtype) {
+ case dns_geoip_country_code:
+ maxlen = 2;
+ goto getcountry;
+
+ case dns_geoip_country_code3:
+ maxlen = 3;
+ goto getcountry;
+
+ case dns_geoip_country_name:
+ maxlen = 255;
+ getcountry:
+ db = DB46(reqaddr, geoip, country);
+ if (db == NULL)
+ return (false);
+
+ INSIST(elt->as_string != NULL);
+
+ cs = country_lookup(db, subtype, family, ipnum, ipnum6, scope);
+ if (cs != NULL && strncasecmp(elt->as_string, cs, maxlen) == 0)
+ return (true);
+ break;
+
+ case dns_geoip_city_countrycode:
+ case dns_geoip_city_countrycode3:
+ case dns_geoip_city_countryname:
+ case dns_geoip_city_region:
+ case dns_geoip_city_regionname:
+ case dns_geoip_city_name:
+ case dns_geoip_city_postalcode:
+ case dns_geoip_city_continentcode:
+ case dns_geoip_city_timezonecode:
+ INSIST(elt->as_string != NULL);
+
+ db = DB46(reqaddr, geoip, city);
+ if (db == NULL)
+ return (false);
+
+ record = city_lookup(db, subtype, family,
+ ipnum, ipnum6, scope);
+ if (record == NULL)
+ break;
+
+ s = city_string(record, subtype, &maxlen);
+ INSIST(maxlen != 0);
+ if (s != NULL && strncasecmp(elt->as_string, s, maxlen) == 0)
+ return (true);
+ break;
+
+ case dns_geoip_city_metrocode:
+ db = DB46(reqaddr, geoip, city);
+ if (db == NULL)
+ return (false);
+
+ record = city_lookup(db, subtype, family,
+ ipnum, ipnum6, scope);
+ if (record == NULL)
+ break;
+
+ if (elt->as_int == record->metro_code)
+ return (true);
+ break;
+
+ case dns_geoip_city_areacode:
+ db = DB46(reqaddr, geoip, city);
+ if (db == NULL)
+ return (false);
+
+ record = city_lookup(db, subtype, family,
+ ipnum, ipnum6, scope);
+ if (record == NULL)
+ break;
+
+ if (elt->as_int == record->area_code)
+ return (true);
+ break;
+
+ case dns_geoip_region_countrycode:
+ case dns_geoip_region_code:
+ case dns_geoip_region_name:
+ case dns_geoip_region:
+ if (geoip->region == NULL)
+ return (false);
+
+ INSIST(elt->as_string != NULL);
+
+ /* Region DB is not supported for IPv6 */
+ if (family == AF_INET6)
+ return (false);
+
+ region = region_lookup(geoip->region, subtype, ipnum, scope);
+ if (region == NULL)
+ break;
+
+ s = region_string(region, subtype, &maxlen);
+ INSIST(maxlen != 0);
+ if (s != NULL && strncasecmp(elt->as_string, s, maxlen) == 0)
+ return (true);
+ break;
+
+ case dns_geoip_isp_name:
+ db = geoip->isp;
+ goto getname;
+
+ case dns_geoip_org_name:
+ db = geoip->org;
+ goto getname;
+
+ case dns_geoip_as_asnum:
+ db = geoip->as;
+ goto getname;
+
+ case dns_geoip_domain_name:
+ db = geoip->domain;
+
+ getname:
+ if (db == NULL)
+ return (false);
+
+ INSIST(elt->as_string != NULL);
+ /* ISP, Org, AS, and Domain are not supported for IPv6 */
+ if (family == AF_INET6)
+ return (false);
+
+ s = name_lookup(db, subtype, ipnum, scope);
+ if (s != NULL) {
+ size_t l;
+ if (strcasecmp(elt->as_string, s) == 0)
+ return (true);
+ if (subtype != dns_geoip_as_asnum)
+ break;
+ /*
+ * Just check if the ASNNNN value matches.
+ */
+ l = strlen(elt->as_string);
+ if (l > 0U && strchr(elt->as_string, ' ') == NULL &&
+ strncasecmp(elt->as_string, s, l) == 0 &&
+ s[l] == ' ')
+ return (true);
+ }
+ break;
+
+ case dns_geoip_netspeed_id:
+ INSIST(geoip->netspeed != NULL);
+
+ /* Netspeed DB is not supported for IPv6 */
+ if (family == AF_INET6)
+ return (false);
+
+ id = netspeed_lookup(geoip->netspeed, subtype, ipnum, scope);
+ if (id == elt->as_int)
+ return (true);
+ break;
+
+ case dns_geoip_countrycode:
+ case dns_geoip_countrycode3:
+ case dns_geoip_countryname:
+ case dns_geoip_regionname:
+ /*
+ * If these were not remapped by fix_subtype(),
+ * the database was unavailable. Always return false.
+ */
+ break;
+
+ default:
+ INSIST(0);
+ }
+
+ return (false);
+#endif
+}
+
+void
+dns_geoip_shutdown(void) {
+#ifdef HAVE_GEOIP
+ GeoIP_cleanup();
+#ifdef ISC_PLATFORM_USETHREADS
+ if (state_mctx != NULL)
+ isc_mem_detach(&state_mctx);
+#endif
+#else
+ return;
+#endif
+}
diff --git a/lib/dns/gssapi_link.c b/lib/dns/gssapi_link.c
new file mode 100644
index 0000000..82eac95
--- /dev/null
+++ b/lib/dns/gssapi_link.c
@@ -0,0 +1,388 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <config.h>
+
+#ifdef GSSAPI
+
+#include <stdbool.h>
+
+#include <isc/base64.h>
+#include <isc/buffer.h>
+#include <isc/mem.h>
+#include <isc/print.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)
+
+#define GBUFFER_TO_REGION(gb, r) \
+ do { \
+ (r).length = (unsigned int)(gb).length; \
+ (r).base = (gb).value; \
+ } 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, (unsigned int)gsig.length);
+ if (gsig.length != 0U)
+ 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);
+ memmove(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 bool
+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 (gsskey1 == gsskey2);
+}
+
+static isc_result_t
+gssapi_generate(dst_key_t *key, int unused, void (*callback)(int)) {
+ UNUSED(key);
+ UNUSED(unused);
+ UNUSED(callback);
+
+ /* No idea */
+ return (ISC_R_FAILURE);
+}
+
+static bool
+gssapi_isprivate(const dst_key_t *key) {
+ UNUSED(key);
+ return (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 isc_result_t
+gssapi_restore(dst_key_t *key, const char *keystr) {
+ OM_uint32 major, minor;
+ unsigned int len;
+ isc_buffer_t *b = NULL;
+ isc_region_t r;
+ gss_buffer_desc gssbuffer;
+ isc_result_t result;
+
+ len = strlen(keystr);
+ if ((len % 4) != 0U)
+ return (ISC_R_BADBASE64);
+
+ len = (len / 4) * 3;
+
+ result = isc_buffer_allocate(key->mctx, &b, len);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = isc_base64_decodestring(keystr, b);
+ if (result != ISC_R_SUCCESS) {
+ isc_buffer_free(&b);
+ return (result);
+ }
+
+ isc_buffer_remainingregion(b, &r);
+ REGION_TO_GBUFFER(r, gssbuffer);
+ major = gss_import_sec_context(&minor, &gssbuffer,
+ &key->keydata.gssctx);
+ if (major != GSS_S_COMPLETE) {
+ isc_buffer_free(&b);
+ return (ISC_R_FAILURE);
+ }
+
+ isc_buffer_free(&b);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+gssapi_dump(dst_key_t *key, isc_mem_t *mctx, char **buffer, int *length) {
+ OM_uint32 major, minor;
+ gss_buffer_desc gssbuffer;
+ size_t len;
+ char *buf;
+ isc_buffer_t b;
+ isc_region_t r;
+ isc_result_t result;
+
+ major = gss_export_sec_context(&minor, &key->keydata.gssctx,
+ &gssbuffer);
+ if (major != GSS_S_COMPLETE) {
+ fprintf(stderr, "gss_export_sec_context -> %d, %d\n",
+ major, minor);
+ return (ISC_R_FAILURE);
+ }
+ if (gssbuffer.length == 0U)
+ return (ISC_R_FAILURE);
+ len = ((gssbuffer.length + 2)/3) * 4;
+ buf = isc_mem_get(mctx, len);
+ if (buf == NULL) {
+ gss_release_buffer(&minor, &gssbuffer);
+ return (ISC_R_NOMEMORY);
+ }
+ isc_buffer_init(&b, buf, (unsigned int)len);
+ GBUFFER_TO_REGION(gssbuffer, r);
+ result = isc_base64_totext(&r, 0, "", &b);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ gss_release_buffer(&minor, &gssbuffer);
+ *buffer = buf;
+ *length = (int)len;
+ return (ISC_R_SUCCESS);
+}
+
+static dst_func_t gssapi_functions = {
+ gssapi_create_signverify_ctx,
+ NULL, /*%< createctx2 */
+ gssapi_destroy_signverify_ctx,
+ gssapi_adddata,
+ gssapi_sign,
+ gssapi_verify,
+ NULL, /*%< verify2 */
+ NULL, /*%< computesecret */
+ gssapi_compare,
+ NULL, /*%< paramcompare */
+ gssapi_generate,
+ gssapi_isprivate,
+ gssapi_destroy,
+ NULL, /*%< todns */
+ NULL, /*%< fromdns */
+ NULL, /*%< tofile */
+ NULL, /*%< parse */
+ NULL, /*%< cleanup */
+ NULL, /*%< fromlabel */
+ gssapi_dump,
+ gssapi_restore,
+};
+
+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..8bd99af
--- /dev/null
+++ b/lib/dns/gssapictx.c
@@ -0,0 +1,894 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#include <config.h>
+
+#include <ctype.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <isc/buffer.h>
+#include <isc/dir.h>
+#include <isc/entropy.h>
+#include <isc/file.h>
+#include <isc/lex.h>
+#include <isc/mem.h>
+#include <isc/once.h>
+#include <isc/print.h>
+#include <isc/platform.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.
+ */
+#ifdef GSSAPI
+#ifdef WIN32
+#include <krb5/krb5.h>
+#else
+#include ISC_PLATFORM_KRB5HEADER
+#endif
+
+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 = (unsigned int)(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_toprincipal(namep, buffer);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ 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 != 0U) {
+ 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
+
+#ifdef GSSAPI
+/*
+ * check for the most common configuration errors.
+ *
+ * The errors checked for are:
+ * - tkey-gssapi-credential doesn't start with DNS/
+ * - the default realm in /etc/krb5.conf and the
+ * tkey-gssapi-credential bind config option don't match
+ *
+ * Note that if tkey-gssapi-keytab is set then these configure checks
+ * are not performed, and runtime errors from gssapi are used instead
+ */
+static void
+check_config(const char *gss_name) {
+ const char *p;
+ krb5_context krb5_ctx;
+ char *krb5_realm_name = NULL;
+
+ if (strncasecmp(gss_name, "DNS/", 4) != 0) {
+ gss_log(ISC_LOG_ERROR, "tkey-gssapi-credential (%s) "
+ "should start with 'DNS/'", gss_name);
+ return;
+ }
+
+ if (krb5_init_context(&krb5_ctx) != 0) {
+ gss_log(ISC_LOG_ERROR, "Unable to initialise krb5 context");
+ return;
+ }
+ if (krb5_get_default_realm(krb5_ctx, &krb5_realm_name) != 0) {
+ gss_log(ISC_LOG_ERROR, "Unable to get krb5 default realm");
+ krb5_free_context(krb5_ctx);
+ return;
+ }
+ p = strchr(gss_name, '@');
+ if (p == NULL) {
+ gss_log(ISC_LOG_ERROR, "badly formatted "
+ "tkey-gssapi-credentials (%s)", gss_name);
+ krb5_free_context(krb5_ctx);
+ return;
+ }
+ if (strcasecmp(p + 1, krb5_realm_name) != 0) {
+ gss_log(ISC_LOG_ERROR, "default realm from krb5.conf (%s) "
+ "does not match tkey-gssapi-credential (%s)",
+ krb5_realm_name, gss_name);
+ krb5_free_context(krb5_ctx);
+ return;
+ }
+ krb5_free_context(krb5_ctx);
+}
+#endif
+
+isc_result_t
+dst_gssapi_acquirecred(dns_name_t *name, bool initiate,
+ gss_cred_id_t *cred)
+{
+#ifdef GSSAPI
+ isc_result_t result;
+ isc_buffer_t namebuf;
+ gss_name_t gname;
+ gss_buffer_desc gnamebuf;
+ unsigned char array[DNS_NAME_MAXTEXT + 1];
+ OM_uint32 gret, minor;
+ 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) {
+ check_config((char *)array);
+
+ 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, NULL, &lifetime);
+
+ if (gret != GSS_S_COMPLETE) {
+ gss_log(3, "failed to acquire %s credentials for %s: %s",
+ initiate ? "initiate" : "accept",
+ (gname != NULL) ? (char *)gnamebuf.value : "?",
+ gss_error_tostring(gret, minor, buf, sizeof(buf)));
+ if (gname != NULL)
+ check_config((char *)array);
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ gss_log(4, "acquired %s credentials for %s",
+ initiate ? "initiate" : "accept",
+ (gname != NULL) ? (char *)gnamebuf.value : "?");
+
+ log_cred(*cred);
+ result = ISC_R_SUCCESS;
+
+cleanup:
+ 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
+ REQUIRE(cred != NULL && *cred == NULL);
+
+ UNUSED(name);
+ UNUSED(initiate);
+ UNUSED(cred);
+
+ return (ISC_R_NOTIMPLEMENTED);
+#endif
+}
+
+bool
+dst_gssapi_identitymatchesrealmkrb5(const dns_name_t *signer,
+ const dns_name_t *name,
+ const dns_name_t *realm,
+ bool subdomain)
+{
+#ifdef GSSAPI
+ char sbuf[DNS_NAME_FORMATSIZE];
+ char rbuf[DNS_NAME_FORMATSIZE];
+ char *sname;
+ char *rname;
+ isc_buffer_t buffer;
+ isc_result_t result;
+
+ /*
+ * It is far, far easier to write the names we are looking at into
+ * a string, and do string operations on them.
+ */
+ isc_buffer_init(&buffer, sbuf, sizeof(sbuf));
+ result = dns_name_toprincipal(signer, &buffer);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ isc_buffer_putuint8(&buffer, 0);
+ 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 = strchr(sbuf, '@');
+ if (rname == NULL)
+ return (false);
+ *rname = '\0';
+ rname++;
+
+ if (strcmp(rname, rbuf) != 0) {
+ return (false);
+ }
+
+ /*
+ * 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 (false);
+ *sname = '\0';
+ sname++;
+ if (strcmp(sbuf, "host") != 0)
+ return (false);
+
+ /*
+ * If name is non NULL check that it matches against the
+ * machine name as expected.
+ */
+ if (name != NULL) {
+ dns_fixedname_t fixed;
+ dns_name_t *machine;
+
+ machine = dns_fixedname_initname(&fixed);
+ result = dns_name_fromstring(machine, sname, 0, NULL);
+ if (result != ISC_R_SUCCESS) {
+ return (false);
+ }
+ if (subdomain) {
+ return (dns_name_issubdomain(name, machine));
+ }
+ return (dns_name_equal(name, machine));
+ }
+
+ return (true);
+#else
+ UNUSED(signer);
+ UNUSED(name);
+ UNUSED(realm);
+ UNUSED(subdomain);
+ return (false);
+#endif
+}
+
+bool
+dst_gssapi_identitymatchesrealmms(const dns_name_t *signer,
+ const dns_name_t *name,
+ const dns_name_t *realm,
+ bool subdomain)
+{
+#ifdef GSSAPI
+ char sbuf[DNS_NAME_FORMATSIZE];
+ char rbuf[DNS_NAME_FORMATSIZE];
+ char *sname;
+ char *rname;
+ isc_buffer_t buffer;
+ isc_result_t result;
+
+ /*
+ * It is far, far easier to write the names we are looking at into
+ * a string, and do string operations on them.
+ */
+ isc_buffer_init(&buffer, sbuf, sizeof(sbuf));
+ result = dns_name_toprincipal(signer, &buffer);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ isc_buffer_putuint8(&buffer, 0);
+ 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 = strchr(sbuf, '@');
+ if (rname == NULL)
+ return (false);
+ sname = strchr(sbuf, '$');
+ if (sname == NULL)
+ return (false);
+
+ /*
+ * Verify that the $ and @ follow one another.
+ */
+ if (rname - sname != 1)
+ return (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++;
+ *sname = '\0';
+ sname = sbuf;
+
+ if (strcmp(rname, rbuf) != 0) {
+ return (false);
+ }
+
+ /*
+ * Now, we check that the realm matches (case sensitive) and that
+ * 'name' matches against 'machinename' qualified with 'realm'.
+ */
+ if (name != NULL) {
+ dns_fixedname_t fixed;
+ dns_name_t *machine;
+
+ machine = dns_fixedname_initname(&fixed);
+ result = dns_name_fromstring2(machine, sbuf, realm, 0, NULL);
+ if (result != ISC_R_SUCCESS) {
+ return (false);
+ }
+ if (subdomain) {
+ return (dns_name_issubdomain(name, machine));
+ }
+ return (dns_name_equal(name, machine));
+ }
+
+ return (true);
+#else
+ UNUSED(signer);
+ UNUSED(name);
+ UNUSED(realm);
+ UNUSED(subdomain);
+ return (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
+}
+
+#ifdef GSSAPI
+/*
+ * Format a gssapi error message info into a char ** on the given memory
+ * context. This is used to return gssapi error messages back up the
+ * call chain for reporting to the user.
+ */
+static void
+gss_err_message(isc_mem_t *mctx, uint32_t major, uint32_t minor,
+ char **err_message)
+{
+ char buf[1024];
+ char *estr;
+
+ if (err_message == NULL || mctx == NULL) {
+ /* the caller doesn't want any error messages */
+ return;
+ }
+
+ estr = gss_error_tostring(major, minor, buf, sizeof(buf));
+ if (estr != NULL)
+ (*err_message) = isc_mem_strdup(mctx, estr);
+}
+#endif
+
+isc_result_t
+dst_gssapi_initctx(dns_name_t *name, isc_buffer_t *intoken,
+ isc_buffer_t *outtoken, gss_ctx_id_t *gssctx,
+ isc_mem_t *mctx, char **err_message)
+{
+#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];
+
+ /* Client must pass us a valid gss_ctx_id_t here */
+ REQUIRE(gssctx != NULL);
+ REQUIRE(mctx != 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) {
+ gss_err_message(mctx, gret, minor, err_message);
+ 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;
+ }
+
+ /*
+ * Note that we don't set GSS_C_SEQUENCE_FLAG as Windows DNS
+ * servers don't like it.
+ */
+ flags = GSS_C_REPLAY_FLAG | GSS_C_MUTUAL_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_err_message(mctx, gret, minor, err_message);
+ if (err_message != NULL && *err_message != NULL)
+ gss_log(3, "Failure initiating security context: %s",
+ *err_message);
+ else
+ gss_log(3, "Failure initiating security context");
+
+ 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 != 0U) {
+ GBUFFER_TO_REGION(gouttoken, r);
+ RETERR(isc_buffer_copyregion(outtoken, &r));
+ }
+
+ if (gret == GSS_S_COMPLETE)
+ result = ISC_R_SUCCESS;
+ else
+ result = DNS_R_CONTINUE;
+
+ out:
+ if (gouttoken.length != 0U)
+ (void)gss_release_buffer(&minor, &gouttoken);
+ (void)gss_release_name(&minor, &gname);
+ return (result);
+#else
+ UNUSED(name);
+ UNUSED(intoken);
+ UNUSED(outtoken);
+ UNUSED(gssctx);
+ UNUSED(mctx);
+ UNUSED(err_message);
+
+ return (ISC_R_NOTIMPLEMENTED);
+#endif
+}
+
+isc_result_t
+dst_gssapi_acceptctx(gss_cred_id_t cred,
+ const char *gssapi_keytab,
+ 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);
+
+ REGION_TO_GBUFFER(*intoken, gintoken);
+
+ if (*ctxout == NULL)
+ context = GSS_C_NO_CONTEXT;
+ else
+ context = *ctxout;
+
+ if (gssapi_keytab != NULL) {
+#if defined(ISC_PLATFORM_GSSAPI_KRB5_HEADER) || defined(WIN32)
+ gret = gsskrb5_register_acceptor_identity(gssapi_keytab);
+ if (gret != GSS_S_COMPLETE) {
+ gss_log(3, "failed "
+ "gsskrb5_register_acceptor_identity(%s): %s",
+ gssapi_keytab,
+ gss_error_tostring(gret, 0, buf, sizeof(buf)));
+ return (DNS_R_INVALIDTKEY);
+ }
+#else
+ /*
+ * Minimize memory leakage by only setting KRB5_KTNAME
+ * if it needs to change.
+ */
+ const char *old = getenv("KRB5_KTNAME");
+ if (old == NULL || strcmp(old, gssapi_keytab) != 0) {
+ size_t size;
+ char *kt;
+
+ size = strlen(gssapi_keytab) + 13;
+ kt = malloc(size);
+ if (kt == NULL)
+ return (ISC_R_NOMEMORY);
+ snprintf(kt, size, "KRB5_KTNAME=%s", gssapi_keytab);
+ if (putenv(kt) != 0)
+ return (ISC_R_NOMEMORY);
+ }
+#endif
+ }
+
+ log_cred(cred);
+
+ 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:
+ case GSS_S_CONTINUE_NEEDED:
+ 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 > 0U) {
+ RETERR(isc_buffer_allocate(mctx, outtoken,
+ (unsigned int)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 > 0U &&
+ ((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,
+ 0, NULL));
+
+ if (gnamebuf.length != 0U) {
+ 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)));
+ }
+ } else
+ result = DNS_R_CONTINUE;
+
+ *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(gssapi_keytab);
+ 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(uint32_t major, 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 != 0U)
+ (void)gss_release_buffer(&minor_stat, &msg_major);
+ if (msg_minor.length != 0U)
+ (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..94e73b1
--- /dev/null
+++ b/lib/dns/hmac_link.c
@@ -0,0 +1,1811 @@
+/*
+ * Portions Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Portions Copyright (C) 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.
+ */
+
+#include <config.h>
+
+#include <stdbool.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/safe.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <pk11/site.h>
+
+#include <dst/result.h>
+
+#include "dst_internal.h"
+#ifdef HAVE_FIPS_MODE
+#include "dst_openssl.h" /* FIPS_mode() prototype */
+#endif
+#include "dst_parse.h"
+
+#ifndef PK11_MD5_DISABLE
+static isc_result_t hmacmd5_fromdns(dst_key_t *key, isc_buffer_t *data);
+
+struct dst_hmacmd5_key {
+ unsigned char key[ISC_MD5_BLOCK_LENGTH];
+};
+#endif
+
+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);
+}
+
+#ifndef PK11_MD5_DISABLE
+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, ISC_MD5_BLOCK_LENGTH);
+ 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 bool
+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 (true);
+ else if (hkey1 == NULL || hkey2 == NULL)
+ return (false);
+
+ if (isc_safe_memequal(hkey1->key, hkey2->key, ISC_MD5_BLOCK_LENGTH))
+ return (true);
+ else
+ return (false);
+}
+
+static isc_result_t
+hmacmd5_generate(dst_key_t *key, int pseudorandom_ok, void (*callback)(int)) {
+ isc_buffer_t b;
+ isc_result_t ret;
+ unsigned int bytes;
+ unsigned char data[ISC_MD5_BLOCK_LENGTH];
+
+ UNUSED(callback);
+
+ bytes = (key->key_size + 7) / 8;
+ if (bytes > ISC_MD5_BLOCK_LENGTH) {
+ bytes = ISC_MD5_BLOCK_LENGTH;
+ key->key_size = ISC_MD5_BLOCK_LENGTH * 8;
+ }
+
+ memset(data, 0, ISC_MD5_BLOCK_LENGTH);
+ ret = dst__entropy_getdata(data, bytes, pseudorandom_ok);
+
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+
+ isc_buffer_init(&b, data, bytes);
+ isc_buffer_add(&b, bytes);
+ ret = hmacmd5_fromdns(key, &b);
+ isc_safe_memwipe(data, sizeof(data));
+
+ return (ret);
+}
+
+static bool
+hmacmd5_isprivate(const dst_key_t *key) {
+ UNUSED(key);
+ return (true);
+}
+
+static void
+hmacmd5_destroy(dst_key_t *key) {
+ dst_hmacmd5_key_t *hkey = key->keydata.hmacmd5;
+
+ isc_safe_memwipe(hkey, sizeof(*hkey));
+ isc_mem_put(key->mctx, hkey, sizeof(*hkey));
+ 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 > ISC_MD5_BLOCK_LENGTH) {
+ isc_md5_init(&md5ctx);
+ isc_md5_update(&md5ctx, r.base, r.length);
+ isc_md5_final(&md5ctx, hkey->key);
+ keylen = ISC_MD5_DIGESTLENGTH;
+ } else {
+ memmove(hkey->key, r.base, r.length);
+ keylen = r.length;
+ }
+
+ key->key_size = keylen * 8;
+ key->keydata.hmacmd5 = hkey;
+
+ isc_buffer_forward(data, r.length);
+
+ 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);
+
+ if (key->external)
+ return (DST_R_EXTERNALKEY);
+
+ 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_key_t *pub) {
+ dst_private_t priv;
+ isc_result_t result, tresult;
+ isc_buffer_t b;
+ isc_mem_t *mctx = key->mctx;
+ unsigned int i;
+
+ UNUSED(pub);
+ /* read private key file */
+ result = dst__privstruct_parse(key, DST_ALG_HMACMD5, lexer, mctx,
+ &priv);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ if (key->external)
+ result = DST_R_EXTERNALKEY;
+
+ 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);
+ isc_safe_memwipe(&priv, sizeof(priv));
+ return (result);
+}
+
+static dst_func_t hmacmd5_functions = {
+ hmacmd5_createctx,
+ NULL, /*%< createctx2 */
+ hmacmd5_destroyctx,
+ hmacmd5_adddata,
+ hmacmd5_sign,
+ hmacmd5_verify,
+ NULL, /*%< verify2 */
+ NULL, /*%< computesecret */
+ hmacmd5_compare,
+ NULL, /*%< paramcompare */
+ hmacmd5_generate,
+ hmacmd5_isprivate,
+ hmacmd5_destroy,
+ hmacmd5_todns,
+ hmacmd5_fromdns,
+ hmacmd5_tofile,
+ hmacmd5_parse,
+ NULL, /*%< cleanup */
+ NULL, /*%< fromlabel */
+ NULL, /*%< dump */
+ NULL, /*%< restore */
+};
+
+isc_result_t
+dst__hmacmd5_init(dst_func_t **funcp) {
+#ifdef HAVE_FIPS_MODE
+ /*
+ * Problems from OpenSSL are likely from FIPS mode
+ */
+ int fips_mode = FIPS_mode();
+
+ if (fips_mode != 0) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "FIPS mode is %d: MD5 is only supported "
+ "if the value is 0.\n"
+ "Please disable either FIPS mode or MD5.",
+ fips_mode);
+ }
+#endif
+
+ /*
+ * Prevent use of incorrect crypto
+ */
+
+ RUNTIME_CHECK(isc_md5_check(false));
+ RUNTIME_CHECK(isc_hmacmd5_check(0));
+
+ REQUIRE(funcp != NULL);
+ if (*funcp == NULL)
+ *funcp = &hmacmd5_functions;
+ return (ISC_R_SUCCESS);
+}
+#endif
+
+static isc_result_t hmacsha1_fromdns(dst_key_t *key, isc_buffer_t *data);
+
+struct dst_hmacsha1_key {
+ unsigned char key[ISC_SHA1_BLOCK_LENGTH];
+};
+
+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_BLOCK_LENGTH);
+ 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 bool
+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 (true);
+ else if (hkey1 == NULL || hkey2 == NULL)
+ return (false);
+
+ if (isc_safe_memequal(hkey1->key, hkey2->key, ISC_SHA1_BLOCK_LENGTH))
+ return (true);
+ else
+ return (false);
+}
+
+static isc_result_t
+hmacsha1_generate(dst_key_t *key, int pseudorandom_ok, void (*callback)(int)) {
+ isc_buffer_t b;
+ isc_result_t ret;
+ unsigned int bytes;
+ unsigned char data[ISC_SHA1_BLOCK_LENGTH];
+
+ UNUSED(callback);
+
+ bytes = (key->key_size + 7) / 8;
+ if (bytes > ISC_SHA1_BLOCK_LENGTH) {
+ bytes = ISC_SHA1_BLOCK_LENGTH;
+ key->key_size = ISC_SHA1_BLOCK_LENGTH * 8;
+ }
+
+ memset(data, 0, ISC_SHA1_BLOCK_LENGTH);
+ ret = dst__entropy_getdata(data, bytes, pseudorandom_ok);
+
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+
+ isc_buffer_init(&b, data, bytes);
+ isc_buffer_add(&b, bytes);
+ ret = hmacsha1_fromdns(key, &b);
+ isc_safe_memwipe(data, sizeof(data));
+
+ return (ret);
+}
+
+static bool
+hmacsha1_isprivate(const dst_key_t *key) {
+ UNUSED(key);
+ return (true);
+}
+
+static void
+hmacsha1_destroy(dst_key_t *key) {
+ dst_hmacsha1_key_t *hkey = key->keydata.hmacsha1;
+
+ isc_safe_memwipe(hkey, sizeof(*hkey));
+ isc_mem_put(key->mctx, hkey, sizeof(*hkey));
+ 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_BLOCK_LENGTH) {
+ isc_sha1_init(&sha1ctx);
+ isc_sha1_update(&sha1ctx, r.base, r.length);
+ isc_sha1_final(&sha1ctx, hkey->key);
+ keylen = ISC_SHA1_DIGESTLENGTH;
+ } else {
+ memmove(hkey->key, r.base, r.length);
+ keylen = r.length;
+ }
+
+ key->key_size = keylen * 8;
+ key->keydata.hmacsha1 = hkey;
+
+ isc_buffer_forward(data, r.length);
+
+ 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);
+
+ if (key->external)
+ return (DST_R_EXTERNALKEY);
+
+ 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_key_t *pub) {
+ dst_private_t priv;
+ isc_result_t result, tresult;
+ isc_buffer_t b;
+ isc_mem_t *mctx = key->mctx;
+ unsigned int i;
+
+ UNUSED(pub);
+ /* read private key file */
+ result = dst__privstruct_parse(key, DST_ALG_HMACSHA1, lexer, mctx,
+ &priv);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ if (key->external)
+ result = DST_R_EXTERNALKEY;
+
+ key->key_bits = 0;
+ for (i = 0; i < priv.nelements && result == ISC_R_SUCCESS; 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);
+ isc_safe_memwipe(&priv, sizeof(priv));
+ return (result);
+}
+
+static dst_func_t hmacsha1_functions = {
+ hmacsha1_createctx,
+ NULL, /*%< createctx2 */
+ hmacsha1_destroyctx,
+ hmacsha1_adddata,
+ hmacsha1_sign,
+ hmacsha1_verify,
+ NULL, /* verify2 */
+ NULL, /* computesecret */
+ hmacsha1_compare,
+ NULL, /* paramcompare */
+ hmacsha1_generate,
+ hmacsha1_isprivate,
+ hmacsha1_destroy,
+ hmacsha1_todns,
+ hmacsha1_fromdns,
+ hmacsha1_tofile,
+ hmacsha1_parse,
+ NULL, /* cleanup */
+ NULL, /* fromlabel */
+ NULL, /* dump */
+ NULL, /* restore */
+};
+
+isc_result_t
+dst__hmacsha1_init(dst_func_t **funcp) {
+ /*
+ * Prevent use of incorrect crypto
+ */
+ RUNTIME_CHECK(isc_sha1_check(false));
+ RUNTIME_CHECK(isc_hmacsha1_check(0));
+
+ 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_BLOCK_LENGTH];
+};
+
+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_BLOCK_LENGTH);
+ 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 bool
+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 (true);
+ else if (hkey1 == NULL || hkey2 == NULL)
+ return (false);
+
+ if (isc_safe_memequal(hkey1->key, hkey2->key, ISC_SHA224_BLOCK_LENGTH))
+ return (true);
+ else
+ return (false);
+}
+
+static isc_result_t
+hmacsha224_generate(dst_key_t *key, int pseudorandom_ok,
+ void (*callback)(int))
+{
+ isc_buffer_t b;
+ isc_result_t ret;
+ unsigned int bytes;
+ unsigned char data[ISC_SHA224_BLOCK_LENGTH];
+
+ UNUSED(callback);
+
+ bytes = (key->key_size + 7) / 8;
+ if (bytes > ISC_SHA224_BLOCK_LENGTH) {
+ bytes = ISC_SHA224_BLOCK_LENGTH;
+ key->key_size = ISC_SHA224_BLOCK_LENGTH * 8;
+ }
+
+ memset(data, 0, ISC_SHA224_BLOCK_LENGTH);
+ ret = dst__entropy_getdata(data, bytes, pseudorandom_ok);
+
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+
+ isc_buffer_init(&b, data, bytes);
+ isc_buffer_add(&b, bytes);
+ ret = hmacsha224_fromdns(key, &b);
+ isc_safe_memwipe(data, sizeof(data));
+
+ return (ret);
+}
+
+static bool
+hmacsha224_isprivate(const dst_key_t *key) {
+ UNUSED(key);
+ return (true);
+}
+
+static void
+hmacsha224_destroy(dst_key_t *key) {
+ dst_hmacsha224_key_t *hkey = key->keydata.hmacsha224;
+
+ isc_safe_memwipe(hkey, sizeof(*hkey));
+ isc_mem_put(key->mctx, hkey, sizeof(*hkey));
+ 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_BLOCK_LENGTH) {
+ isc_sha224_init(&sha224ctx);
+ isc_sha224_update(&sha224ctx, r.base, r.length);
+ isc_sha224_final(hkey->key, &sha224ctx);
+ keylen = ISC_SHA224_DIGESTLENGTH;
+ } else {
+ memmove(hkey->key, r.base, r.length);
+ keylen = r.length;
+ }
+
+ key->key_size = keylen * 8;
+ key->keydata.hmacsha224 = hkey;
+
+ isc_buffer_forward(data, r.length);
+
+ 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);
+
+ if (key->external)
+ return (DST_R_EXTERNALKEY);
+
+ 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_key_t *pub) {
+ dst_private_t priv;
+ isc_result_t result, tresult;
+ isc_buffer_t b;
+ isc_mem_t *mctx = key->mctx;
+ unsigned int i;
+
+ UNUSED(pub);
+ /* read private key file */
+ result = dst__privstruct_parse(key, DST_ALG_HMACSHA224, lexer, mctx,
+ &priv);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ if (key->external)
+ result = DST_R_EXTERNALKEY;
+
+ key->key_bits = 0;
+ for (i = 0; i < priv.nelements && result == ISC_R_SUCCESS; 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);
+ isc_safe_memwipe(&priv, sizeof(priv));
+ return (result);
+}
+
+static dst_func_t hmacsha224_functions = {
+ hmacsha224_createctx,
+ NULL, /*%< createctx2 */
+ hmacsha224_destroyctx,
+ hmacsha224_adddata,
+ hmacsha224_sign,
+ hmacsha224_verify,
+ NULL, /* verify2 */
+ NULL, /* computesecret */
+ hmacsha224_compare,
+ NULL, /* paramcompare */
+ hmacsha224_generate,
+ hmacsha224_isprivate,
+ hmacsha224_destroy,
+ hmacsha224_todns,
+ hmacsha224_fromdns,
+ hmacsha224_tofile,
+ hmacsha224_parse,
+ NULL, /* cleanup */
+ NULL, /* fromlabel */
+ NULL, /* dump */
+ NULL, /* restore */
+};
+
+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_BLOCK_LENGTH];
+};
+
+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_BLOCK_LENGTH);
+ 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 bool
+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 (true);
+ else if (hkey1 == NULL || hkey2 == NULL)
+ return (false);
+
+ if (isc_safe_memequal(hkey1->key, hkey2->key, ISC_SHA256_BLOCK_LENGTH))
+ return (true);
+ else
+ return (false);
+}
+
+static isc_result_t
+hmacsha256_generate(dst_key_t *key, int pseudorandom_ok,
+ void (*callback)(int))
+{
+ isc_buffer_t b;
+ isc_result_t ret;
+ unsigned int bytes;
+ unsigned char data[ISC_SHA256_BLOCK_LENGTH];
+
+ UNUSED(callback);
+
+ bytes = (key->key_size + 7) / 8;
+ if (bytes > ISC_SHA256_BLOCK_LENGTH) {
+ bytes = ISC_SHA256_BLOCK_LENGTH;
+ key->key_size = ISC_SHA256_BLOCK_LENGTH * 8;
+ }
+
+ memset(data, 0, ISC_SHA256_BLOCK_LENGTH);
+ ret = dst__entropy_getdata(data, bytes, pseudorandom_ok);
+
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+
+ isc_buffer_init(&b, data, bytes);
+ isc_buffer_add(&b, bytes);
+ ret = hmacsha256_fromdns(key, &b);
+ isc_safe_memwipe(data, sizeof(data));
+
+ return (ret);
+}
+
+static bool
+hmacsha256_isprivate(const dst_key_t *key) {
+ UNUSED(key);
+ return (true);
+}
+
+static void
+hmacsha256_destroy(dst_key_t *key) {
+ dst_hmacsha256_key_t *hkey = key->keydata.hmacsha256;
+
+ isc_safe_memwipe(hkey, sizeof(*hkey));
+ isc_mem_put(key->mctx, hkey, sizeof(*hkey));
+ 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_BLOCK_LENGTH) {
+ isc_sha256_init(&sha256ctx);
+ isc_sha256_update(&sha256ctx, r.base, r.length);
+ isc_sha256_final(hkey->key, &sha256ctx);
+ keylen = ISC_SHA256_DIGESTLENGTH;
+ } else {
+ memmove(hkey->key, r.base, r.length);
+ keylen = r.length;
+ }
+
+ key->key_size = keylen * 8;
+ key->keydata.hmacsha256 = hkey;
+
+ isc_buffer_forward(data, r.length);
+
+ 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);
+
+ if (key->external)
+ return (DST_R_EXTERNALKEY);
+
+ 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_key_t *pub) {
+ dst_private_t priv;
+ isc_result_t result, tresult;
+ isc_buffer_t b;
+ isc_mem_t *mctx = key->mctx;
+ unsigned int i;
+
+ UNUSED(pub);
+ /* read private key file */
+ result = dst__privstruct_parse(key, DST_ALG_HMACSHA256, lexer, mctx,
+ &priv);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ if (key->external)
+ result = DST_R_EXTERNALKEY;
+
+ key->key_bits = 0;
+ for (i = 0; i < priv.nelements && result == ISC_R_SUCCESS; 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);
+ isc_safe_memwipe(&priv, sizeof(priv));
+ return (result);
+}
+
+static dst_func_t hmacsha256_functions = {
+ hmacsha256_createctx,
+ NULL, /*%< createctx2 */
+ hmacsha256_destroyctx,
+ hmacsha256_adddata,
+ hmacsha256_sign,
+ hmacsha256_verify,
+ NULL, /* verify2 */
+ NULL, /* computesecret */
+ hmacsha256_compare,
+ NULL, /* paramcompare */
+ hmacsha256_generate,
+ hmacsha256_isprivate,
+ hmacsha256_destroy,
+ hmacsha256_todns,
+ hmacsha256_fromdns,
+ hmacsha256_tofile,
+ hmacsha256_parse,
+ NULL, /* cleanup */
+ NULL, /* fromlabel */
+ NULL, /* dump */
+ NULL, /* restore */
+};
+
+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_BLOCK_LENGTH];
+};
+
+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_BLOCK_LENGTH);
+ 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 bool
+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 (true);
+ else if (hkey1 == NULL || hkey2 == NULL)
+ return (false);
+
+ if (isc_safe_memequal(hkey1->key, hkey2->key, ISC_SHA384_BLOCK_LENGTH))
+ return (true);
+ else
+ return (false);
+}
+
+static isc_result_t
+hmacsha384_generate(dst_key_t *key, int pseudorandom_ok,
+ void (*callback)(int))
+{
+ isc_buffer_t b;
+ isc_result_t ret;
+ unsigned int bytes;
+ unsigned char data[ISC_SHA384_BLOCK_LENGTH];
+
+ UNUSED(callback);
+
+ bytes = (key->key_size + 7) / 8;
+ if (bytes > ISC_SHA384_BLOCK_LENGTH) {
+ bytes = ISC_SHA384_BLOCK_LENGTH;
+ key->key_size = ISC_SHA384_BLOCK_LENGTH * 8;
+ }
+
+ memset(data, 0, ISC_SHA384_BLOCK_LENGTH);
+ ret = dst__entropy_getdata(data, bytes, pseudorandom_ok);
+
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+
+ isc_buffer_init(&b, data, bytes);
+ isc_buffer_add(&b, bytes);
+ ret = hmacsha384_fromdns(key, &b);
+ isc_safe_memwipe(data, sizeof(data));
+
+ return (ret);
+}
+
+static bool
+hmacsha384_isprivate(const dst_key_t *key) {
+ UNUSED(key);
+ return (true);
+}
+
+static void
+hmacsha384_destroy(dst_key_t *key) {
+ dst_hmacsha384_key_t *hkey = key->keydata.hmacsha384;
+
+ isc_safe_memwipe(hkey, sizeof(*hkey));
+ isc_mem_put(key->mctx, hkey, sizeof(*hkey));
+ 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_BLOCK_LENGTH) {
+ isc_sha384_init(&sha384ctx);
+ isc_sha384_update(&sha384ctx, r.base, r.length);
+ isc_sha384_final(hkey->key, &sha384ctx);
+ keylen = ISC_SHA384_DIGESTLENGTH;
+ } else {
+ memmove(hkey->key, r.base, r.length);
+ keylen = r.length;
+ }
+
+ key->key_size = keylen * 8;
+ key->keydata.hmacsha384 = hkey;
+
+ isc_buffer_forward(data, r.length);
+
+ 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);
+
+ if (key->external)
+ return (DST_R_EXTERNALKEY);
+
+ 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_key_t *pub) {
+ dst_private_t priv;
+ isc_result_t result, tresult;
+ isc_buffer_t b;
+ isc_mem_t *mctx = key->mctx;
+ unsigned int i;
+
+ UNUSED(pub);
+ /* read private key file */
+ result = dst__privstruct_parse(key, DST_ALG_HMACSHA384, lexer, mctx,
+ &priv);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ if (key->external)
+ result = DST_R_EXTERNALKEY;
+
+ key->key_bits = 0;
+ for (i = 0; i < priv.nelements && result == ISC_R_SUCCESS; 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);
+ isc_safe_memwipe(&priv, sizeof(priv));
+ return (result);
+}
+
+static dst_func_t hmacsha384_functions = {
+ hmacsha384_createctx,
+ NULL, /*%< createctx2 */
+ hmacsha384_destroyctx,
+ hmacsha384_adddata,
+ hmacsha384_sign,
+ hmacsha384_verify,
+ NULL, /* verify2 */
+ NULL, /* computesecret */
+ hmacsha384_compare,
+ NULL, /* paramcompare */
+ hmacsha384_generate,
+ hmacsha384_isprivate,
+ hmacsha384_destroy,
+ hmacsha384_todns,
+ hmacsha384_fromdns,
+ hmacsha384_tofile,
+ hmacsha384_parse,
+ NULL, /* cleanup */
+ NULL, /* fromlabel */
+ NULL, /* dump */
+ NULL, /* restore */
+};
+
+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_BLOCK_LENGTH];
+};
+
+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_BLOCK_LENGTH);
+ 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 bool
+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 (true);
+ else if (hkey1 == NULL || hkey2 == NULL)
+ return (false);
+
+ if (isc_safe_memequal(hkey1->key, hkey2->key, ISC_SHA512_BLOCK_LENGTH))
+ return (true);
+ else
+ return (false);
+}
+
+static isc_result_t
+hmacsha512_generate(dst_key_t *key, int pseudorandom_ok,
+ void (*callback)(int))
+{
+ isc_buffer_t b;
+ isc_result_t ret;
+ unsigned int bytes;
+ unsigned char data[ISC_SHA512_BLOCK_LENGTH];
+
+ UNUSED(callback);
+
+ bytes = (key->key_size + 7) / 8;
+ if (bytes > ISC_SHA512_BLOCK_LENGTH) {
+ bytes = ISC_SHA512_BLOCK_LENGTH;
+ key->key_size = ISC_SHA512_BLOCK_LENGTH * 8;
+ }
+
+ memset(data, 0, ISC_SHA512_BLOCK_LENGTH);
+ ret = dst__entropy_getdata(data, bytes, pseudorandom_ok);
+
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+
+ isc_buffer_init(&b, data, bytes);
+ isc_buffer_add(&b, bytes);
+ ret = hmacsha512_fromdns(key, &b);
+ isc_safe_memwipe(data, sizeof(data));
+
+ return (ret);
+}
+
+static bool
+hmacsha512_isprivate(const dst_key_t *key) {
+ UNUSED(key);
+ return (true);
+}
+
+static void
+hmacsha512_destroy(dst_key_t *key) {
+ dst_hmacsha512_key_t *hkey = key->keydata.hmacsha512;
+
+ isc_safe_memwipe(hkey, sizeof(*hkey));
+ isc_mem_put(key->mctx, hkey, sizeof(*hkey));
+ 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_BLOCK_LENGTH) {
+ isc_sha512_init(&sha512ctx);
+ isc_sha512_update(&sha512ctx, r.base, r.length);
+ isc_sha512_final(hkey->key, &sha512ctx);
+ keylen = ISC_SHA512_DIGESTLENGTH;
+ } else {
+ memmove(hkey->key, r.base, r.length);
+ keylen = r.length;
+ }
+
+ key->key_size = keylen * 8;
+ key->keydata.hmacsha512 = hkey;
+
+ isc_buffer_forward(data, r.length);
+
+ 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);
+
+ if (key->external)
+ return (DST_R_EXTERNALKEY);
+
+ 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_key_t *pub) {
+ dst_private_t priv;
+ isc_result_t result, tresult;
+ isc_buffer_t b;
+ isc_mem_t *mctx = key->mctx;
+ unsigned int i;
+
+ UNUSED(pub);
+ /* read private key file */
+ result = dst__privstruct_parse(key, DST_ALG_HMACSHA512, lexer, mctx,
+ &priv);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ if (key->external)
+ result = DST_R_EXTERNALKEY;
+
+ key->key_bits = 0;
+ for (i = 0; i < priv.nelements && result == ISC_R_SUCCESS; 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);
+ isc_safe_memwipe(&priv, sizeof(priv));
+ return (result);
+}
+
+static dst_func_t hmacsha512_functions = {
+ hmacsha512_createctx,
+ NULL, /*%< createctx2 */
+ hmacsha512_destroyctx,
+ hmacsha512_adddata,
+ hmacsha512_sign,
+ hmacsha512_verify,
+ NULL, /* verify2 */
+ NULL, /* computesecret */
+ hmacsha512_compare,
+ NULL, /* paramcompare */
+ hmacsha512_generate,
+ hmacsha512_isprivate,
+ hmacsha512_destroy,
+ hmacsha512_todns,
+ hmacsha512_fromdns,
+ hmacsha512_tofile,
+ hmacsha512_parse,
+ NULL, /* cleanup */
+ NULL, /* fromlabel */
+ NULL, /* dump */
+ NULL, /* restore */
+};
+
+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..5467ea1
--- /dev/null
+++ b/lib/dns/include/Makefile.in
@@ -0,0 +1,17 @@
+# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# See the COPYRIGHT file distributed with this work for additional
+# information regarding copyright ownership.
+
+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..59897fe
--- /dev/null
+++ b/lib/dns/include/dns/Makefile.in
@@ -0,0 +1,60 @@
+# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# See the COPYRIGHT file distributed with this work for additional
+# information regarding copyright ownership.
+
+srcdir = @srcdir@
+VPATH = @srcdir@
+top_srcdir = @top_srcdir@
+
+VERSION=@BIND9_VERSION@
+
+HEADERS = acache.h acl.h adb.h badcache.h bit.h byaddr.h \
+ cache.h callbacks.h catz.h cert.h \
+ client.h clientinfo.h compress.h \
+ db.h dbiterator.h dbtable.h diff.h dispatch.h \
+ dlz.h dlz_dlopen.h dns64.h dnssec.h ds.h dsdigest.h \
+ dnstap.h dyndb.h \
+ edns.h ecdb.h events.h fixedname.h forward.h geoip.h \
+ ipkeylist.h iptable.h \
+ journal.h keydata.h keyflags.h keytable.h keyvalues.h \
+ lib.h lookup.h log.h master.h masterdump.h message.h \
+ name.h ncache.h nsec.h nsec3.h nta.h opcode.h order.h \
+ peer.h portlist.h private.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 rpz.h rriterator.h rrl.h \
+ sdb.h sdlz.h secalg.h secproto.h soa.h ssu.h stats.h \
+ tcpmsg.h time.h timer.h tkey.h tsec.h tsig.h ttl.h types.h \
+ update.h validator.h version.h view.h xfrin.h \
+ zone.h zonekey.h zt.h
+
+GENHEADERS = @DNSTAP_PB_C_H@ 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 || exit 1; \
+ done
+ for i in ${GENHEADERS}; do \
+ ${INSTALL_DATA} $$i ${DESTDIR}${includedir}/dns || exit 1; \
+ done
+
+uninstall::
+ for i in ${GENHEADERS}; do \
+ rm -f ${DESTDIR}${includedir}/dns/$$i || exit 1; \
+ done
+ for i in ${HEADERS}; do \
+ rm -f ${DESTDIR}${includedir}/dns/$$i || exit 1; \
+ done
diff --git a/lib/dns/include/dns/acache.h b/lib/dns/include/dns/acache.h
new file mode 100644
index 0000000..7b1dcd7
--- /dev/null
+++ b/lib/dns/include/dns/acache.h
@@ -0,0 +1,442 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $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/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, size_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
+ */
+
+bool
+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.
+ *
+ * Returns:
+ * true if the entry was active when canceled
+ */
+
+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..c8a7833
--- /dev/null
+++ b/lib/dns/include/dns/acl.h
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $Id: acl.h,v 1.35 2011/06/17 23:47:49 tbox Exp $ */
+
+#ifndef DNS_ACL_H
+#define DNS_ACL_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file dns/acl.h
+ * \brief
+ * Address match list handling.
+ */
+
+/***
+ *** Imports
+ ***/
+
+#include <stdbool.h>
+
+#include <isc/lang.h>
+#include <isc/magic.h>
+#include <isc/netaddr.h>
+#include <isc/refcount.h>
+
+#ifdef HAVE_GEOIP
+#include <dns/geoip.h>
+#endif
+#include <dns/name.h>
+#include <dns/types.h>
+#include <dns/iptable.h>
+
+#ifdef HAVE_GEOIP
+#include <GeoIP.h>
+#endif
+
+/***
+ *** Types
+ ***/
+
+typedef enum {
+ dns_aclelementtype_ipprefix,
+ dns_aclelementtype_keyname,
+ dns_aclelementtype_nestedacl,
+ dns_aclelementtype_localhost,
+ dns_aclelementtype_localnets,
+#ifdef HAVE_GEOIP
+ dns_aclelementtype_geoip,
+#endif /* HAVE_GEOIP */
+ dns_aclelementtype_any
+} dns_aclelementtype_t;
+
+typedef struct dns_aclipprefix dns_aclipprefix_t;
+
+struct dns_aclipprefix {
+ isc_netaddr_t address; /* IP4/IP6 */
+ unsigned int prefixlen;
+};
+
+struct dns_aclelement {
+ dns_aclelementtype_t type;
+ bool negative;
+ dns_name_t keyname;
+#ifdef HAVE_GEOIP
+ dns_geoip_elem_t geoip_elem;
+#endif /* HAVE_GEOIP */
+ 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;
+ bool 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;
+ bool match_mapped;
+#ifdef HAVE_GEOIP
+ dns_geoip_databases_t *geoip;
+ bool geoip_use_ecs;
+#endif
+};
+
+#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.
+ */
+
+bool
+dns_acl_isany(dns_acl_t *acl);
+/*%<
+ * Test whether ACL is set to "{ any; }"
+ */
+
+bool
+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, bool 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 positive match in the parent ACL.
+ */
+
+void
+dns_acl_attach(dns_acl_t *source, dns_acl_t **target);
+/*%<
+ * Attach to acl 'source'.
+ *
+ * Requires:
+ *\li 'source' to be a valid acl.
+ *\li 'target' to be non NULL and '*target' to be NULL.
+ */
+
+void
+dns_acl_detach(dns_acl_t **aclp);
+/*%<
+ * Detach the acl. On final detach the acl must not be linked on any
+ * list.
+ *
+ * Requires:
+ *\li '*aclp' to be a valid acl.
+ *
+ * Insists:
+ *\li '*aclp' is not linked on final detach.
+ */
+
+bool
+dns_acl_isinsecure(const dns_acl_t *a);
+/*%<
+ * Return #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 #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);
+
+isc_result_t
+dns_acl_match2(const isc_netaddr_t *reqaddr,
+ const dns_name_t *reqsigner,
+ const isc_netaddr_t *ecs,
+ uint8_t ecslen,
+ uint8_t *scope,
+ 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',
+ * and optionally the client prefix 'ecs' of length 'ecslen'
+ * (reported via EDNS client subnet option) against 'acl'.
+ *
+ * 'reqsigner' and 'ecs' may be NULL. If an ACL matches against 'ecs'
+ * and 'ecslen', then 'scope' will be set to indicate the netmask that
+ * matched.
+ *
+ * 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.
+ *
+ * 'env' points to the current ACL environment, including the
+ * current values of localhost and localnets and (if applicable)
+ * the GeoIP context.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS Always succeeds.
+ */
+
+bool
+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);
+
+bool
+dns_aclelement_match2(const isc_netaddr_t *reqaddr,
+ const dns_name_t *reqsigner,
+ const isc_netaddr_t *ecs,
+ uint8_t ecslen,
+ uint8_t *scope,
+ 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 true iff it matched.
+ *
+ * To determine whether the match was positive 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..ca35bac
--- /dev/null
+++ b/lib/dns/include/dns/adb.h
@@ -0,0 +1,841 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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 <inttypes.h>
+#include <stdbool.h>
+
+#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 server's fetch quota is exceeded; it will be treated as
+ * lame for this query.
+ */
+#define DNS_ADBFIND_OVERQUOTA 0x00000400
+
+/*%
+ * 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] microsecs */
+ isc_dscp_t dscp;
+
+ 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);
+isc_result_t
+dns_adb_createfind2(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,
+ unsigned int depth, isc_counter_t *qc,
+ 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.
+ */
+
+/*
+ * Reasonable defaults for RTT adjustments
+ *
+ * (Note: these values function both as scaling factors and as
+ * indicators of the type of RTT adjustment operation taking place.
+ * Adjusting the scaling factors is fine, as long as they all remain
+ * unique values.)
+ */
+#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.
+ *
+ * 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_agesrtt(dns_adb_t *adb, dns_adbaddrinfo_t *addr, isc_stdtime_t now);
+/*
+ * dns_adb_agesrtt is equivalent to dns_adb_adjustsrtt with factor
+ * equal to DNS_ADB_RTTADJAGE and the current time passed in.
+ *
+ * Requires:
+ *
+ *\li adb be valid.
+ *
+ *\li addr be valid.
+ *
+ * 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.
+ */
+
+void
+dns_adb_setudpsize(dns_adb_t *adb, dns_adbaddrinfo_t *addr, unsigned int size);
+/*%
+ * Update seen UDP response size. The largest seen will be returned by
+ * dns_adb_getudpsize().
+ *
+ * Requires:
+ *
+ *\li adb be valid.
+ *
+ *\li addr be valid.
+ */
+
+unsigned int
+dns_adb_getudpsize(dns_adb_t *adb, dns_adbaddrinfo_t *addr);
+/*%
+ * Return the largest seen UDP response size.
+ *
+ * Requires:
+ *
+ *\li adb be valid.
+ *
+ *\li addr be valid.
+ */
+
+unsigned int
+dns_adb_probesize(dns_adb_t *adb, dns_adbaddrinfo_t *addr);
+unsigned int
+dns_adb_probesize2(dns_adb_t *adb, dns_adbaddrinfo_t *addr, int lookups);
+/*%
+ * Return suggested EDNS UDP size based on observed responses / failures.
+ * 'lookups' is the number of times the current lookup has been attempted.
+ *
+ * Requires:
+ *
+ *\li adb be valid.
+ *
+ *\li addr be valid.
+ */
+
+void
+dns_adb_plainresponse(dns_adb_t *adb, dns_adbaddrinfo_t *addr);
+/*%
+ * Record a successful plain DNS response.
+ *
+ * Requires:
+ *
+ *\li adb be valid.
+ *
+ *\li addr be valid.
+ */
+
+void
+dns_adb_timeout(dns_adb_t *adb, dns_adbaddrinfo_t *addr);
+/*%
+ * Record a plain DNS UDP query failed.
+ *
+ * Requires:
+ *
+ *\li adb be valid.
+ *
+ *\li addr be valid.
+ */
+
+void
+dns_adb_ednsto(dns_adb_t *adb, dns_adbaddrinfo_t *addr, unsigned int size);
+/*%
+ * Record a failed EDNS UDP response and the advertised EDNS UDP buffer size
+ * used.
+ *
+ * Requires:
+ *
+ *\li adb be valid.
+ *
+ *\li addr be valid.
+ */
+
+bool
+dns_adb_noedns(dns_adb_t *adb, dns_adbaddrinfo_t *addr);
+/*%
+ * Return whether EDNS should be disabled for this server.
+ *
+ * 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, size_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.
+ */
+
+void
+dns_adb_flushnames(dns_adb_t *adb, dns_name_t *name);
+/*%<
+ * Flush 'name' and all subdomains from the adb cache.
+ *
+ * Requires:
+ *\li 'adb' is valid.
+ *\li 'name' is valid.
+ */
+
+void
+dns_adb_setcookie(dns_adb_t *adb, dns_adbaddrinfo_t *addr,
+ const unsigned char *cookie, size_t len);
+/*%<
+ * Record the COOKIE associated with this addresss. If
+ * cookie is NULL or len is zero the recorded COOKIE is cleared.
+ *
+ * Requires:
+ *\li 'adb' is valid.
+ *\li 'addr' is valid.
+ */
+
+size_t
+dns_adb_getcookie(dns_adb_t *adb, dns_adbaddrinfo_t *addr,
+ unsigned char *cookie, size_t len);
+/*
+ * Retieve the saved COOKIE value and store it in 'cookie' which has
+ * size 'len'.
+ *
+ * Requires:
+ *\li 'adb' is valid.
+ *\li 'addr' is valid.
+ *
+ * Returns:
+ * The size of the cookie or zero if it doesn't fit in the buffer
+ * or it doesn't exist.
+ */
+
+void
+dns_adb_setquota(dns_adb_t *adb, uint32_t quota, uint32_t freq,
+ double low, double high, double discount);
+/*%<
+ * Set the baseline ADB quota, and configure parameters for the
+ * quota adjustment algorithm.
+ *
+ * If the number of fetches currently waiting for responses from this
+ * address exceeds the current quota, then additional fetches are spilled.
+ *
+ * 'quota' is the highest permissible quota; it will adjust itself
+ * downward in response to detected congestion.
+ *
+ * After every 'freq' fetches have either completed or timed out, an
+ * exponentially weighted moving average of the ratio of timeouts
+ * to responses is calculated. If the EWMA goes above a 'high'
+ * threshold, then the quota is adjusted down one step; if it drops
+ * below a 'low' threshold, then the quota is adjusted back up one
+ * step.
+ *
+ * The quota adjustment is based on the function (1 / 1 + (n/10)^(3/2)),
+ * for values of n from 0 to 99. It starts at 100% of the baseline
+ * quota, and descends after 100 steps to 2%.
+ *
+ * 'discount' represents the discount rate of the moving average. Higher
+ * values cause older values to be discounted sooner, providing a faster
+ * response to changes in the timeout ratio.
+ *
+ * Requires:
+ *\li 'adb' is valid.
+ */
+
+bool
+dns_adbentry_overquota(dns_adbentry_t *entry);
+/*%<
+ * Returns true if the specified ADB has too many active fetches.
+ *
+ * Requires:
+ *\li 'entry' is valid.
+ */
+
+void
+dns_adb_beginudpfetch(dns_adb_t *adb, dns_adbaddrinfo_t *addr);
+void
+dns_adb_endudpfetch(dns_adb_t *adb, dns_adbaddrinfo_t *addr);
+/*%
+ * Begin/end a UDP fetch on a particular address.
+ *
+ * These functions increment or decrement the fetch counter for
+ * the ADB entry so that the fetch quota can be enforced.
+ *
+ * Requires:
+ *
+ *\li adb be valid.
+ *
+ *\li addr be valid.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_ADB_H */
diff --git a/lib/dns/include/dns/badcache.h b/lib/dns/include/dns/badcache.h
new file mode 100644
index 0000000..276e3cb
--- /dev/null
+++ b/lib/dns/include/dns/badcache.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef DNS_BADCACHE_H
+#define DNS_BADCACHE_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file dns/badcache.h
+ * \brief
+ * Defines dns_badcache_t, the "bad cache" object.
+ *
+ * Notes:
+ *\li A bad cache object is a hash table of name/type tuples,
+ * indicating whether a given tuple known to be "bad" in some
+ * sense (e.g., queries for that name and type have been
+ * returning SERVFAIL). This is used for both the "bad server
+ * cache" in the resolver and for the "servfail cache" in
+ * the view.
+ *
+ * Reliability:
+ *
+ * Resources:
+ *
+ * Security:
+ *
+ * Standards:
+ */
+
+/***
+ *** Imports
+ ***/
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+/***
+ *** Functions
+ ***/
+
+isc_result_t
+dns_badcache_init(isc_mem_t *mctx, unsigned int size, dns_badcache_t **bcp);
+/*%
+ * Allocate and initialize a badcache and store it in '*bcp'.
+ *
+ * Requires:
+ * \li mctx != NULL
+ * \li bcp != NULL
+ * \li *bcp == NULL
+ */
+
+void
+dns_badcache_destroy(dns_badcache_t **bcp);
+/*%
+ * Flush and then free badcache in 'bcp'. '*bcp' is set to NULL on return.
+ *
+ * Requires:
+ * \li '*bcp' to be a valid badcache
+ */
+
+void
+dns_badcache_add(dns_badcache_t *bc, dns_name_t *name,
+ dns_rdatatype_t type, bool update,
+ uint32_t flags, isc_time_t *expire);
+/*%
+ * Adds a badcache entry to the badcache 'bc' for name 'name' and
+ * type 'type'. If an entry already exists, then it will be updated if
+ * 'update' is true. The entry will be stored with flags 'flags'
+ * and expiration date 'expire'.
+ *
+ * Requires:
+ * \li bc to be a valid badcache.
+ * \li name != NULL
+ * \li expire != NULL
+ */
+
+bool
+dns_badcache_find(dns_badcache_t *bc, dns_name_t *name,
+ dns_rdatatype_t type, uint32_t *flagp,
+ isc_time_t *now);
+/*%
+ * Returns true if a record is found in the badcache 'bc' matching
+ * 'name' and 'type', with an expiration date later than 'now'.
+ * If 'flagp' is not NULL, then '*flagp' is updated to the flags
+ * that were stored in the badcache entry. Returns false if
+ * no matching record is found.
+ *
+ * Requires:
+ * \li bc to be a valid badcache.
+ * \li name != NULL
+ * \li now != NULL
+ */
+
+void
+dns_badcache_flush(dns_badcache_t *bc);
+/*%
+ * Flush the entire bad cache.
+ *
+ * Requires:
+ * \li bc to be a valid badcache
+ */
+
+void
+dns_badcache_flushname(dns_badcache_t *bc, dns_name_t *name);
+/*%
+ * Flush the bad cache of all entries at 'name'.
+ *
+ * Requires:
+ * \li bc to be a valid badcache
+ * \li name != NULL
+ */
+
+void
+dns_badcache_flushtree(dns_badcache_t *bc, dns_name_t *name);
+/*%
+ * Flush the bad cache of all entries at or below 'name'.
+ *
+ * Requires:
+ * \li bc to be a valid badcache
+ * \li name != NULL
+ */
+
+void
+dns_badcache_print(dns_badcache_t *bc, const char *cachename, FILE *fp);
+/*%
+ * Print the contents of badcache 'bc' (headed by the title 'cachename')
+ * to file pointer 'fp'.
+ *
+ * Requires:
+ * \li bc to be a valid badcache
+ * \li cachename != NULL
+ * \li fp != NULL
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_BADCACHE_H */
diff --git a/lib/dns/include/dns/bit.h b/lib/dns/include/dns/bit.h
new file mode 100644
index 0000000..9ebb57a
--- /dev/null
+++ b/lib/dns/include/dns/bit.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#ifndef DNS_BIT_H
+#define DNS_BIT_H 1
+
+/*! \file dns/bit.h */
+
+#include <inttypes.h>
+
+typedef 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) \
+ ((*(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..9fe3009
--- /dev/null
+++ b/lib/dns/include/dns/byaddr.h
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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 <stdbool.h>
+
+#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, bool 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..62797db
--- /dev/null
+++ b/lib/dns/include/dns/cache.h
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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 <stdbool.h>
+
+#include <isc/json.h>
+#include <isc/lang.h>
+#include <isc/stats.h>
+#include <isc/stdtime.h>
+
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+/***
+ *** Functions
+ ***/
+
+isc_result_t
+dns_cache_create(isc_mem_t *cmctx, 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
+dns_cache_create2(isc_mem_t *cmctx, isc_taskmgr_t *taskmgr,
+ isc_timermgr_t *timermgr, dns_rdataclass_t rdclass,
+ const char *cachename, const char *db_type,
+ unsigned int db_argc, char **db_argv, dns_cache_t **cachep);
+isc_result_t
+dns_cache_create3(isc_mem_t *cmctx, isc_mem_t *hmctx, isc_taskmgr_t *taskmgr,
+ isc_timermgr_t *timermgr, dns_rdataclass_t rdclass,
+ const char *cachename, const char *db_type,
+ unsigned int db_argc, char **db_argv, dns_cache_t **cachep);
+/*%<
+ * Create a new DNS cache.
+ *
+ * dns_cache_create2() will create a named cache.
+ *
+ * dns_cache_create3() will create a named cache using two separate memory
+ * contexts, one for cache data which can be cleaned and a separate one for
+ * memory allocated for the heap (which can grow without an upper limit and
+ * has no mechanism for shrinking).
+ *
+ * dns_cache_create() is a backward compatible version that internally
+ * specifies an empty cache name and a single memory context.
+ *
+ * Requires:
+ *
+ *\li 'cmctx' (and 'hmctx' if applicable) 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 'cachename' is a valid string. This must not be NULL.
+ *
+ *\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.
+ */
+
+unsigned int
+dns_cache_getcleaninginterval(dns_cache_t *cache);
+/*%<
+ * Get the periodic cache cleaning interval to 'interval' seconds.
+ */
+
+const char *
+dns_cache_getname(dns_cache_t *cache);
+/*%<
+ * Get the cache name.
+ */
+
+void
+dns_cache_setcachesize(dns_cache_t *cache, size_t size);
+/*%<
+ * Set the maximum cache size. 0 means unlimited.
+ */
+
+size_t
+dns_cache_getcachesize(dns_cache_t *cache);
+/*%<
+ * Get the maximum cache size.
+ */
+
+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_flushnode(dns_cache_t *cache, dns_name_t *name,
+ bool tree);
+/*
+ * Flush a given name from the cache. If 'tree' is true, then
+ * also flush all names under 'name'.
+ *
+ * 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_result_t
+dns_cache_flushname(dns_cache_t *cache, dns_name_t *name);
+/*
+ * Flush a given name from the cache. Equivalent to
+ * dns_cache_flushpartial(cache, name, false).
+ *
+ * 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_stats_t *
+dns_cache_getstats(dns_cache_t *cache);
+/*
+ * Return a pointer to the stats collection object for 'cache'
+ */
+
+void
+dns_cache_dumpstats(dns_cache_t *cache, FILE *fp);
+/*
+ * Dump cache statistics and status in text to 'fp'
+ */
+
+void
+dns_cache_updatestats(dns_cache_t *cache, isc_result_t result);
+/*
+ * Update cache statistics based on result code in 'result'
+ */
+
+#ifdef HAVE_LIBXML2
+int
+dns_cache_renderxml(dns_cache_t *cache, xmlTextWriterPtr writer);
+/*
+ * Render cache statistics and status in XML for 'writer'.
+ */
+#endif /* HAVE_LIBXML2 */
+
+#ifdef HAVE_JSON
+isc_result_t
+dns_cache_renderjson(dns_cache_t *cache, json_object *cstats);
+/*
+ * Render cache statistics and status in JSON
+ */
+#endif /* HAVE_JSON */
+
+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..9c723f3
--- /dev/null
+++ b/lib/dns/include/dns/callbacks.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#ifndef DNS_CALLBACKS_H
+#define DNS_CALLBACKS_H 1
+
+/*! \file dns/callbacks.h */
+
+/***
+ *** Imports
+ ***/
+
+#include <isc/lang.h>
+#include <isc/magic.h>
+
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+/***
+ *** Types
+ ***/
+
+#define DNS_CALLBACK_MAGIC ISC_MAGIC('C','L','L','B')
+#define DNS_CALLBACK_VALID(cb) ISC_MAGIC_VALID(cb, DNS_CALLBACK_MAGIC)
+
+struct dns_rdatacallbacks {
+ unsigned int magic;
+
+ /*%
+ * dns_load_master calls this when it has rdatasets to commit.
+ */
+ dns_addrdatasetfunc_t add;
+
+ /*%
+ * This is called when reading in a database image from a 'map'
+ * format zone file.
+ */
+ dns_deserializefunc_t deserialize;
+
+ /*%
+ * dns_master_load*() call this when loading a raw zonefile,
+ * to pass back information obtained from the file header
+ */
+ dns_rawdatafunc_t rawdata;
+ dns_zone_t *zone;
+
+ /*%
+ * 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 *deserialize_private;
+ void *error_private;
+ void *warn_private;
+};
+
+/***
+ *** Initialization
+ ***/
+
+void
+dns_rdatacallbacks_init(dns_rdatacallbacks_t *callbacks);
+/*%<
+ * Initialize 'callbacks'.
+ *
+ * \li 'magic' is set to DNS_CALLBACK_MAGIC
+ *
+ * \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/catz.h b/lib/dns/include/dns/catz.h
new file mode 100644
index 0000000..61426f7
--- /dev/null
+++ b/lib/dns/include/dns/catz.h
@@ -0,0 +1,469 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#ifndef DNS_CATZ_H
+#define DNS_CATZ_H 1
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/ht.h>
+#include <isc/lang.h>
+#include <isc/rwlock.h>
+#include <isc/time.h>
+#include <isc/timer.h>
+
+#include <dns/db.h>
+#include <dns/fixedname.h>
+#include <dns/ipkeylist.h>
+#include <dns/rdata.h>
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+#define DNS_CATZ_ERROR_LEVEL ISC_LOG_WARNING
+#define DNS_CATZ_INFO_LEVEL ISC_LOG_INFO
+#define DNS_CATZ_DEBUG_LEVEL1 ISC_LOG_DEBUG(1)
+#define DNS_CATZ_DEBUG_LEVEL2 ISC_LOG_DEBUG(2)
+#define DNS_CATZ_DEBUG_LEVEL3 ISC_LOG_DEBUG(3)
+#define DNS_CATZ_DEBUG_QUIET (DNS_CATZ_DEBUG_LEVEL3+1)
+
+/*
+ * Catalog Zones functions and structures.
+ */
+
+/*
+ * Options for a member zone in a catalog
+ */
+struct dns_catz_entry_options {
+ /*
+ * Options that can be overriden in catalog zone
+ */
+ /* default-masters definition */
+ dns_ipkeylist_t masters;
+
+ /* both as text in config format, NULL if none */
+ isc_buffer_t *allow_query;
+ isc_buffer_t *allow_transfer;
+
+ /*
+ * Options that are only set in named.conf
+ */
+ /* zone-directory definition */
+ char *zonedir;
+
+ /* zone should not be stored on disk (no 'file' statement in def */
+ bool in_memory;
+ /*
+ * Minimal interval between catalog zone updates, if a new version
+ * of catalog zone is received before this time the update will be
+ * postponed. This is a global option for the whole catalog zone.
+ */
+ uint32_t min_update_interval;
+};
+
+void
+dns_catz_options_init(dns_catz_options_t *options);
+/*%<
+ * Initialize 'options' to NULL values.
+ *
+ * Requires:
+ * \li options to be non NULL
+ */
+
+void
+dns_catz_options_free(dns_catz_options_t *options, isc_mem_t *mctx);
+/*%<
+ * Free 'options' contents into 'mctx'. ('options' itself is not freed.)
+ *
+ * Requires:
+ * \li options to be non NULL
+ * \li mctx to be a valid memory context
+ */
+
+isc_result_t
+dns_catz_options_copy(isc_mem_t *mctx, const dns_catz_options_t *opts,
+ dns_catz_options_t *nopts);
+/*%<
+ * Duplicate 'opts' into 'nopts', allocating space from 'mctx'
+ *
+ * Requires:
+ * \li 'mctx' to be a valid memory context
+ * \li 'options' to be non NULL and valid options
+ * \li 'nopts' to be non NULL
+ */
+
+isc_result_t
+dns_catz_options_setdefault(isc_mem_t *mctx, const dns_catz_options_t *defaults,
+ dns_catz_options_t *opts);
+/*%<
+ * Replace empty values in 'opts' with values from 'defaults'
+ *
+ * Requires:
+ * \li mctx to be a valid memory context
+ * \li defaults to be non NULL and valid options
+ * \li opts to be non NULL
+ */
+
+dns_name_t *
+dns_catz_entry_getname(dns_catz_entry_t *entry);
+/*%<
+ * Get domain name for 'entry'
+ *
+ * Requires:
+ * \li entry to be non NULL
+ *
+ * Returns:
+ * \li domain name for entry
+ */
+
+isc_result_t
+dns_catz_entry_new(isc_mem_t *mctx, const dns_name_t *domain,
+ dns_catz_entry_t **nentryp);
+/*%<
+ * Allocate a new catz_entry on 'mctx', with the name 'domain'
+ *
+ * Requires:
+ * \li mctx to be a valid memory context
+ * \li domain to be valid dns_name or NULL
+ * \li nentryp to be non NULL, *nentryp to be NULL
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS on success
+ * \li ISC_R_NOMEMORY on allocation failure
+ */
+
+isc_result_t
+dns_catz_entry_copy(dns_catz_zone_t *zone, const dns_catz_entry_t *entry,
+ dns_catz_entry_t **nentryp);
+/*%<
+ * Allocate a new catz_entry and deep copy 'entry' into 'nentryp'.
+ *
+ * Requires:
+ * \li mctx to be a valid memory context
+ * \li entry to be non NULL
+ * \li nentryp to be non NULL, *nentryp to be NULL
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS on success
+ * \li ISC_R_NOMEMORY on allocation failure
+ */
+
+void
+dns_catz_entry_attach(dns_catz_entry_t *entry, dns_catz_entry_t **entryp);
+/*%<
+ * Attach an entry
+ *
+ * Requires:
+ * \li entry is not NULL
+ * \li entryp is not NULL, *entryp is NULL
+ */
+
+void
+dns_catz_entry_detach(dns_catz_zone_t *zone, dns_catz_entry_t **entryp);
+/*%<
+ * Detach an entry, free if no further references
+ *
+ * Requires:
+ * \li zone is not NULL
+ * \li entryp is not NULL, *entryp is not NULL
+ */
+
+bool
+dns_catz_entry_validate(const dns_catz_entry_t *entry);
+/*%<
+ * Validate whether entry is correct.
+ * (NOT YET IMPLEMENTED: always returns true)
+ */
+
+bool
+dns_catz_entry_cmp(const dns_catz_entry_t *ea, const dns_catz_entry_t *eb);
+/*%<
+ * Deep compare two entries
+ *
+ * Requires:
+ * \li ea is not NULL
+ * \li eb is not NULL
+ *
+ * Returns:
+ * \li true if entries are the same
+ * \li false if the entries differ
+ */
+
+void
+dns_catz_zone_attach(dns_catz_zone_t *zone, dns_catz_zone_t **zonep);
+/*%<
+ * Attach a catzone
+ *
+ * Requires:
+ * \li zone is not NULL
+ * \li zonep is not NULL, *zonep is NULL
+ */
+
+void
+dns_catz_zone_detach(dns_catz_zone_t** zonep);
+/*%<
+ * Detach a zone, free if no further references
+ *
+ * Requires:
+ * \li zonep is not NULL, *zonep is not NULL
+ */
+
+isc_result_t
+dns_catz_new_zone(dns_catz_zones_t *catzs, dns_catz_zone_t **zonep,
+ const dns_name_t *name);
+/*%<
+ * Allocate a new catz zone on catzs mctx
+ *
+ * Requires:
+ * \li catzs is not NULL
+ * \li zonep is not NULL, *zonep is NULL
+ * \li name is not NULL
+ *
+ */
+
+dns_name_t *
+dns_catz_zone_getname(dns_catz_zone_t *zone);
+/*%<
+ * Get catalog zone name
+ *
+ * Requires:
+ * \li zone is not NULL
+ */
+
+dns_catz_options_t *
+dns_catz_zone_getdefoptions(dns_catz_zone_t *zone);
+/*%<
+ * Get default member zone options for catalog zone 'zone'
+ *
+ * Requires:
+ * \li zone is not NULL
+ */
+
+void
+dns_catz_zone_resetdefoptions(dns_catz_zone_t *zone);
+/*%<
+ * Reset the default member zone options for catalog zone 'zone' to
+ * the default values.
+ *
+ * Requires:
+ * \li zone is not NULL
+ */
+
+isc_result_t
+dns_catz_zones_merge(dns_catz_zone_t *target, dns_catz_zone_t *newzone);
+/*%<
+ * Merge 'newzone' into 'target', calling addzone/delzone/modzone
+ * (from zone->catzs->zmm) for appropriate member zones.
+ *
+ * Requires:
+ * \li orig is not NULL
+ * \li newzone is not NULL, *newzone is not NULL
+ *
+ */
+
+isc_result_t
+dns_catz_update_process(dns_catz_zones_t *catzs, dns_catz_zone_t *zone,
+ dns_name_t *src_name, dns_rdataset_t *rdataset);
+/*%<
+ * Process a single rdataset from a catalog zone 'zone' update, src_name is the
+ * record name.
+ *
+ * Requires:
+ * \li catzs is not NULL
+ * \li zone is not NULL
+ * \li src_name is not NULL
+ * \li rdataset is valid
+ */
+
+isc_result_t
+dns_catz_generate_masterfilename(dns_catz_zone_t *zone, dns_catz_entry_t *entry,
+ isc_buffer_t **buffer);
+/*%<
+ * Generate master file name and put it into *buffer (might be reallocated).
+ * The general format of the file name is:
+ * __catz__catalog.zone.name__member_zone_name.db
+ * But if it's too long it's shortened to:
+ * __catz__unique_hash_generated_from_the_above.db
+ *
+ * Requires:
+ * \li zone is not NULL
+ * \li entry is not NULL
+ * \li buffer is not NULL and *buffer is not NULL
+ */
+
+isc_result_t
+dns_catz_generate_zonecfg(dns_catz_zone_t *zone, dns_catz_entry_t *entry,
+ isc_buffer_t **buf);
+/*%<
+ * Generate a zone config entry (in text form) from dns_catz_entry and puts
+ * it into *buf. buf might be reallocated.
+ *
+ * Requires:
+ * \li zone is not NULL
+ * \li entry is not NULL
+ * \li buf is not NULL
+ * \li *buf is NULL
+ *
+ */
+
+
+/* Methods provided by named to dynamically modify the member zones */
+/* xxxwpk TODO config! */
+typedef isc_result_t (*dns_catz_zoneop_fn_t)(dns_catz_entry_t *entry,
+ dns_catz_zone_t *origin, dns_view_t *view,
+ isc_taskmgr_t *taskmgr, void *udata);
+struct dns_catz_zonemodmethods {
+ dns_catz_zoneop_fn_t addzone;
+ dns_catz_zoneop_fn_t modzone;
+ dns_catz_zoneop_fn_t delzone;
+ void * udata;
+};
+
+
+isc_result_t
+dns_catz_new_zones(dns_catz_zones_t **catzsp, dns_catz_zonemodmethods_t *zmm,
+ isc_mem_t *mctx, isc_taskmgr_t *taskmgr,
+ isc_timermgr_t *timermgr);
+/*%<
+ * Allocate a new catz_zones object, a collection storing all catalog zones
+ * for a view.
+ *
+ * Requires:
+ * \li catzsp is not NULL, *catzsp is NULL
+ * \li zmm is not NULL
+ *
+ */
+
+isc_result_t
+dns_catz_add_zone(dns_catz_zones_t *catzs, const dns_name_t *name,
+ dns_catz_zone_t **catzp);
+/*%<
+ * Allocate a new catz named 'name' and put it in 'catzs' collection.
+ *
+ * Requires:
+ * \li catzs is not NULL
+ * \li name is not NULL
+ * \li zonep is not NULL, *zonep is NULL
+ *
+ */
+
+dns_catz_zone_t *
+dns_catz_get_zone(dns_catz_zones_t *catzs, const dns_name_t *name);
+/*%<
+ * Returns a zone named 'name' from collection 'catzs'
+ *
+ * Requires:
+ * \li catzs is not NULL
+ * \li name is not NULL
+ */
+
+void
+dns_catz_catzs_attach(dns_catz_zones_t *catzs, dns_catz_zones_t **catzsp);
+/*%<
+ * Attach 'catzs' to 'catzsp'
+ *
+ * Requires:
+ * \li catzs is not NULL
+ * \li catzsp is not NULL, *catzsp is NULL
+ */
+
+void
+dns_catz_catzs_detach(dns_catz_zones_t **catzsp);
+/*%<
+ * Detach 'catzsp', free if no further references
+ *
+ * Requires:
+ * \li catzsp is not NULL, *catzsp is not NULL
+ */
+
+void
+dns_catz_catzs_set_view(dns_catz_zones_t *catzs, dns_view_t *view);
+/*%<
+ * Set a view for catzs
+ *
+ * Requires:
+ * \li catzs is not NULL
+ * \li catzs->view is NULL or catzs->view == view
+ */
+
+
+isc_result_t
+dns_catz_dbupdate_callback(dns_db_t *db, void *fn_arg);
+/*%<
+ * Callback for update of catalog zone database.
+ * If there was no catalog zone update recently it launches an
+ * update_taskaction immediately.
+ * If there was an update recently it schedules update_taskaction for some time
+ * in the future.
+ * If there is an update scheduled it replaces old db version with a new one.
+ *
+ * Requires:
+ * \li db is a valid database
+ * \li fn_arg is not NULL (casted to dns_catz_zones_t*)
+ */
+
+void
+dns_catz_update_taskaction(isc_task_t *task, isc_event_t *event);
+/*%<
+ * Task that launches dns_catz_update_from_db
+ *
+ * Requires:
+ * \li event is not NULL
+ */
+
+void
+dns_catz_update_from_db(dns_db_t *db, dns_catz_zones_t *catzs);
+/*%<
+ * Process an updated database for a catalog zone.
+ * It creates a new catz, iterates over database to fill it with content, and
+ * then merges new catz into old catz.
+ *
+ * Requires:
+ * \li db is a valid DB
+ * \li catzs is not NULL
+ *
+ */
+
+void
+dns_catz_prereconfig(dns_catz_zones_t *catzs);
+/*%<
+ * Called before reconfig, clears 'active' flag on all the zones in set
+ *
+ * Requires:
+ * \li catzs is not NULL
+ *
+ */
+
+void
+dns_catz_postreconfig(dns_catz_zones_t *catzs);
+/*%<
+ * Called after reconfig, walks through all zones in set, removes those
+ * inactive and force reload of those with changed configuration.
+ *
+ * Requires:
+ * \li catzs is not NULL
+ */
+
+isc_result_t
+dns_catz_get_iterator(dns_catz_zone_t *catz, isc_ht_iter_t **itp);
+/*%<
+ * Get the hashtable iterator on catalog zone members, point '*itp' to it.
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS -- success
+ * \li Any other value -- failure
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_CATZ_H_ */
diff --git a/lib/dns/include/dns/cert.h b/lib/dns/include/dns/cert.h
new file mode 100644
index 0000000..188d283
--- /dev/null
+++ b/lib/dns/include/dns/cert.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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/client.h b/lib/dns/include/dns/client.h
new file mode 100644
index 0000000..d3c5790
--- /dev/null
+++ b/lib/dns/include/dns/client.h
@@ -0,0 +1,674 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#ifndef DNS_CLIENT_H
+#define DNS_CLIENT_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file
+ *
+ * \brief
+ * The DNS client module provides convenient programming interfaces to various
+ * DNS services, such as name resolution with or without DNSSEC validation or
+ * dynamic DNS update. This module is primarily expected to be used by other
+ * applications than BIND9-related ones that need such advanced DNS features.
+ *
+ * MP:
+ *\li In the typical usage of this module, application threads will not share
+ * the same data structures created and manipulated in this module.
+ * However, the module still ensures appropriate synchronization of such
+ * data structures.
+ *
+ * Resources:
+ *\li TBS
+ *
+ * Security:
+ *\li This module does not handle any low-level data directly, and so no
+ * security issue specific to this module is anticipated.
+ */
+
+#include <isc/event.h>
+#include <isc/sockaddr.h>
+
+#include <dns/tsig.h>
+#include <dns/types.h>
+
+#include <dst/dst.h>
+
+typedef enum {
+ updateop_none = 0,
+ updateop_add = 1,
+ updateop_delete = 2,
+ updateop_exist = 3,
+ updateop_notexist = 4,
+ updateop_max = 5
+} dns_client_updateop_t;
+
+ISC_LANG_BEGINDECLS
+
+/***
+ *** Types
+ ***/
+
+/*%
+ * Optional flags for dns_client_create(x).
+ */
+/*%< Enable caching resolution results (experimental). */
+#define DNS_CLIENTCREATEOPT_USECACHE 0x8000
+
+/*%
+ * Optional flags for dns_client_(start)resolve.
+ */
+/*%< Do not return DNSSEC data (e.g. RRSIGS) with response. */
+#define DNS_CLIENTRESOPT_NODNSSEC 0x01
+/*%< Allow running external context. */
+#define DNS_CLIENTRESOPT_ALLOWRUN 0x02
+/*%< Don't validate responses. */
+#define DNS_CLIENTRESOPT_NOVALIDATE 0x04
+/*%< Don't set the CD flag on upstream queries. */
+#define DNS_CLIENTRESOPT_NOCDFLAG 0x08
+/*%< Use TCP transport. */
+#define DNS_CLIENTRESOPT_TCP 0x10
+
+/*%
+ * Optional flags for dns_client_(start)request.
+ */
+/*%< Allow running external context. */
+#define DNS_CLIENTREQOPT_ALLOWRUN 0x01
+/*%< Use TCP transport. */
+#define DNS_CLIENTREQOPT_TCP 0x02
+
+/*%
+ * Optional flags for dns_client_(start)update.
+ */
+/*%< Allow running external context. */
+#define DNS_CLIENTUPDOPT_ALLOWRUN 0x01
+/*%< Use TCP transport. */
+#define DNS_CLIENTUPDOPT_TCP 0x02
+
+/*%
+ * A dns_clientresevent_t is sent when name resolution performed by a client
+ * completes. 'result' stores the result code of the entire resolution
+ * procedure. 'vresult' specifically stores the result code of DNSSEC
+ * validation if it is performed. When name resolution successfully completes,
+ * 'answerlist' is typically non empty, containing answer names along with
+ * RRsets. It is the receiver's responsibility to free this list by calling
+ * dns_client_freeresanswer() before freeing the event structure.
+ */
+typedef struct dns_clientresevent {
+ ISC_EVENT_COMMON(struct dns_clientresevent);
+ isc_result_t result;
+ isc_result_t vresult;
+ dns_namelist_t answerlist;
+} dns_clientresevent_t; /* too long? */
+
+/*%
+ * Status of a dynamic update procedure.
+ */
+typedef enum {
+ dns_clientupdatestate_prepare, /*%< no updates have been sent */
+ dns_clientupdatestate_sent, /*%< updates were sent, no response */
+ dns_clientupdatestate_done /*%< update was sent and succeeded */
+} dns_clientupdatestate_t;
+
+/*%
+ * A dns_clientreqevent_t is sent when a DNS request is completed by a client.
+ * 'result' stores the result code of the entire transaction.
+ * If the transaction is successfully completed but the response packet cannot
+ * be parsed, 'result' will store the result code of dns_message_parse().
+ * If the response packet is received, 'rmessage' will contain the response
+ * message, whether it is successfully parsed or not.
+ */
+typedef struct dns_clientreqevent {
+ ISC_EVENT_COMMON(struct dns_clientreqevent);
+ isc_result_t result;
+ dns_message_t *rmessage;
+} dns_clientreqevent_t; /* too long? */
+
+/*%
+ * A dns_clientupdateevent_t is sent when dynamic update performed by a client
+ * completes. 'result' stores the result code of the entire update procedure.
+ * 'state' specifies the status of the update procedure when this event is
+ * sent. This can be used as a hint by the receiver to determine whether
+ * the update attempt was ever made. In particular, if the state is
+ * dns_clientupdatestate_prepare, the receiver can be sure that the requested
+ * update was not applied.
+ */
+typedef struct dns_clientupdateevent {
+ ISC_EVENT_COMMON(struct dns_clientupdateevent);
+ isc_result_t result;
+ dns_clientupdatestate_t state;
+} dns_clientupdateevent_t; /* too long? */
+
+isc_result_t
+dns_client_create(dns_client_t **clientp, unsigned int options);
+
+isc_result_t
+dns_client_createx(isc_mem_t *mctx, isc_appctx_t *actx, isc_taskmgr_t *taskmgr,
+ isc_socketmgr_t *socketmgr, isc_timermgr_t *timermgr,
+ unsigned int options, dns_client_t **clientp);
+
+isc_result_t
+dns_client_createx2(isc_mem_t *mctx, isc_appctx_t *actx,
+ isc_taskmgr_t *taskmgr, isc_socketmgr_t *socketmgr,
+ isc_timermgr_t *timermgr, unsigned int options,
+ dns_client_t **clientp,
+ isc_sockaddr_t *localaddr4, isc_sockaddr_t *localaddr6);
+/*%<
+ * Create a DNS client. These functions create a new client object with
+ * minimal internal resources such as the default 'view' for the IN class and
+ * IPv4/IPv6 dispatches for the view.
+ *
+ * dns_client_createx() takes 'manager' arguments so that the caller can
+ * control the behavior of the client through the underlying event framework.
+ * On the other hand, dns_client_create() simplifies the interface and creates
+ * the managers internally. A DNS client object created via
+ * dns_client_create() is expected to be used by an application that only needs
+ * simple synchronous services or by a thread-based application.
+ *
+ * dns_client_createx2 takes two additional parameters, 'localaddr4' and
+ * 'localaddr6', to specify the local address to use for each family. If
+ * both are set to NULL, then wildcard addresses will be used for both
+ * families. If only one is NULL, then the other address will be used
+ * as the local address, and the other protocol family will not be used.
+ *
+ * If the DNS_CLIENTCREATEOPT_USECACHE flag is set in 'options',
+ * dns_client_create(x) will create a cache database with the view.
+ *
+ * Requires:
+ *
+ *\li 'mctx' is a valid memory context.
+ *
+ *\li 'actx' is a valid application context.
+ *
+ *\li 'taskmgr' is a valid task manager.
+ *
+ *\li 'socketmgr' is a valid socket manager.
+ *
+ *\li 'timermgr' is a valid timer manager.
+ *
+ *\li clientp != NULL && *clientp == NULL.
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS On success.
+ *
+ *\li Anything else Failure.
+ */
+
+void
+dns_client_destroy(dns_client_t **clientp);
+/*%<
+ * Destroy 'client'.
+ *
+ * Requires:
+ *
+ *\li '*clientp' is a valid client.
+ *
+ * Ensures:
+ *
+ *\li *clientp == NULL.
+ */
+
+isc_result_t
+dns_client_setservers(dns_client_t *client, dns_rdataclass_t rdclass,
+ dns_name_t *name_space, isc_sockaddrlist_t *addrs);
+/*%<
+ * Specify a list of addresses of recursive name servers that the client will
+ * use for name resolution. A view for the 'rdclass' class must be created
+ * beforehand. If 'name_space' is non NULL, the specified server will be used
+ * if and only if the query name is a subdomain of 'name_space'. When servers
+ * for multiple 'name_space's are provided, and a query name is covered by
+ * more than one 'name_space', the servers for the best (longest) matching
+ * name_space will be used. If 'name_space' is NULL, it works as if
+ * dns_rootname (.) were specified.
+ *
+ * Requires:
+ *
+ *\li 'client' is a valid client.
+ *
+ *\li 'name_space' is NULL or a valid name.
+ *
+ *\li 'addrs' != NULL.
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS On success.
+ *
+ *\li Anything else Failure.
+ */
+
+isc_result_t
+dns_client_clearservers(dns_client_t *client, dns_rdataclass_t rdclass,
+ dns_name_t *name_space);
+/*%<
+ * Remove configured recursive name servers for the 'rdclass' and 'name_space'
+ * from the client. See the description of dns_client_setservers() for
+ * the requirements about 'rdclass' and 'name_space'.
+ *
+ * Requires:
+ *
+ *\li 'client' is a valid client.
+ *
+ *\li 'name_space' is NULL or a valid name.
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS On success.
+ *
+ *\li Anything else Failure.
+ */
+
+isc_result_t
+dns_client_setdlv(dns_client_t *client, dns_rdataclass_t rdclass,
+ const char *dlvname);
+/*%<
+ * Specify a name to use for DNSSEC lookaside validation.
+ * If a trusted key has been added for that name, then DLV will be
+ * used during validation. If 'dlvname' is NULL, then DLV will no
+ * longer be used for this client.
+ *
+ * Requires:
+ *
+ *\li 'client' is a valid client.
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS On success.
+ *
+ *\li Anything else Failure.
+ */
+
+isc_result_t
+dns_client_resolve(dns_client_t *client, dns_name_t *name,
+ dns_rdataclass_t rdclass, dns_rdatatype_t type,
+ unsigned int options, dns_namelist_t *namelist);
+
+isc_result_t
+dns_client_startresolve(dns_client_t *client, dns_name_t *name,
+ dns_rdataclass_t rdclass, dns_rdatatype_t type,
+ unsigned int options, isc_task_t *task,
+ isc_taskaction_t action, void *arg,
+ dns_clientrestrans_t **transp);
+/*%<
+ * Perform name resolution for 'name', 'rdclass', and 'type'.
+ *
+ * If any trusted keys are configured and the query name is considered to
+ * belong to a secure zone, these functions also validate the responses
+ * using DNSSEC by default. If the DNS_CLIENTRESOPT_NOVALIDATE flag is set
+ * in 'options', DNSSEC validation is disabled regardless of the configured
+ * trusted keys or the query name. With DNS_CLIENTRESOPT_NODNSSEC
+ * DNSSEC data is not returned with response. DNS_CLIENTRESOPT_NOCDFLAG
+ * disables the CD flag on queries, DNS_CLIENTRESOPT_TCP switches to
+ * the TCP (vs. UDP) transport.
+ *
+ * dns_client_resolve() provides a synchronous service. This function starts
+ * name resolution internally and blocks until it completes. On success,
+ * 'namelist' will contain a list of answer names, each of which has
+ * corresponding RRsets. The caller must provide a valid empty list, and
+ * is responsible for freeing the list content via dns_client_freeresanswer().
+ * If the name resolution fails due to an error in DNSSEC validation,
+ * dns_client_resolve() returns the result code indicating the validation
+ * error. Otherwise, it returns the result code of the entire resolution
+ * process, either success or failure.
+ *
+ * It is typically expected that the client object passed to
+ * dns_client_resolve() was created via dns_client_create() and has its own
+ * managers and contexts. However, if the DNS_CLIENTRESOPT_ALLOWRUN flag is
+ * set in 'options', this function performs the synchronous service even if
+ * it does not have its own manager and context structures.
+ *
+ * dns_client_startresolve() is an asynchronous version of dns_client_resolve()
+ * and does not block. When name resolution is completed, 'action' will be
+ * called with the argument of a 'dns_clientresevent_t' object, which contains
+ * the resulting list of answer names (on success). On return, '*transp' is
+ * set to an opaque transaction ID so that the caller can cancel this
+ * resolution process.
+ *
+ * Requires:
+ *
+ *\li 'client' is a valid client.
+ *
+ *\li 'addrs' != NULL.
+ *
+ *\li 'name' is a valid name.
+ *
+ *\li 'namelist' != NULL and is not empty.
+ *
+ *\li 'task' is a valid task.
+ *
+ *\li 'transp' != NULL && *transp == NULL;
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS On success.
+ *
+ *\li Anything else Failure.
+ */
+
+void
+dns_client_cancelresolve(dns_clientrestrans_t *trans);
+/*%<
+ * Cancel an ongoing resolution procedure started via
+ * dns_client_startresolve().
+ *
+ * Notes:
+ *
+ *\li If the resolution procedure has not completed, post its CLIENTRESDONE
+ * event with a result code of #ISC_R_CANCELED.
+ *
+ * Requires:
+ *
+ *\li 'trans' is a valid transaction ID.
+ */
+
+void
+dns_client_destroyrestrans(dns_clientrestrans_t **transp);
+/*%<
+ * Destroy name resolution transaction state identified by '*transp'.
+ *
+ * Requires:
+ *
+ *\li '*transp' is a valid transaction ID.
+ *
+ *\li The caller has received the CLIENTRESDONE event (either because the
+ * resolution completed or because dns_client_cancelresolve() was called).
+ *
+ * Ensures:
+ *
+ *\li *transp == NULL.
+ */
+
+void
+dns_client_freeresanswer(dns_client_t *client, dns_namelist_t *namelist);
+/*%<
+ * Free resources allocated for the content of 'namelist'.
+ *
+ * Requires:
+ *
+ *\li 'client' is a valid client.
+ *
+ *\li 'namelist' != NULL.
+ */
+
+isc_result_t
+dns_client_addtrustedkey(dns_client_t *client, dns_rdataclass_t rdclass,
+ dns_name_t *keyname, isc_buffer_t *keydatabuf);
+/*%<
+ * Add a DNSSEC trusted key for the 'rdclass' class. A view for the 'rdclass'
+ * class must be created beforehand. 'keyname' is the DNS name of the key,
+ * and 'keydatabuf' stores the resource data of the key.
+ *
+ * Requires:
+ *
+ *\li 'client' is a valid client.
+ *
+ *\li 'keyname' is a valid name.
+ *
+ *\li 'keydatabuf' is a valid buffer.
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS On success.
+ *
+ *\li Anything else Failure.
+ */
+
+isc_result_t
+dns_client_request(dns_client_t *client, dns_message_t *qmessage,
+ dns_message_t *rmessage, isc_sockaddr_t *server,
+ unsigned int options, unsigned int parseoptions,
+ dns_tsec_t *tsec, unsigned int timeout,
+ unsigned int udptimeout, unsigned int udpretries);
+
+isc_result_t
+dns_client_startrequest(dns_client_t *client, dns_message_t *qmessage,
+ dns_message_t *rmessage, isc_sockaddr_t *server,
+ unsigned int options, unsigned int parseoptions,
+ dns_tsec_t *tsec, unsigned int timeout,
+ unsigned int udptimeout, unsigned int udpretries,
+ isc_task_t *task, isc_taskaction_t action, void *arg,
+ dns_clientreqtrans_t **transp);
+
+/*%<
+ * Send a DNS request containig a query message 'query' to 'server'.
+ *
+ * 'parseoptions' will be used when the response packet is parsed, and will be
+ * passed to dns_message_parse() via dns_request_getresponse(). See
+ * dns_message_parse() for more details.
+ *
+ * 'tsec' is a transaction security object containing, e.g. a TSIG key for
+ * authenticating the request/response transaction. This is optional and can
+ * be NULL, in which case this library performs the transaction without any
+ * transaction authentication.
+ *
+ * 'timeout', 'udptimeout', and 'udpretries' are passed to
+ * dns_request_createvia3(). See dns_request_createvia3() for more details.
+ *
+ * dns_client_request() provides a synchronous service. This function sends
+ * the request and blocks until a response is received. On success,
+ * 'rmessage' will contain the response message. The caller must provide a
+ * valid initialized message.
+ *
+ * It is usually expected that the client object passed to
+ * dns_client_request() was created via dns_client_create() and has its own
+ * managers and contexts. However, if the DNS_CLIENTREQOPT_ALLOWRUN flag is
+ * set in 'options', this function performs the synchronous service even if
+ * it does not have its own manager and context structures.
+ *
+ * dns_client_startrequest() is an asynchronous version of dns_client_request()
+ * and does not block. When the transaction is completed, 'action' will be
+ * called with the argument of a 'dns_clientreqevent_t' object, which contains
+ * the response message (on success). On return, '*transp' is set to an opaque
+ * transaction ID so that the caller can cancel this request.
+ *
+ * DNS_CLIENTREQOPT_TCP switches to the TCP (vs. UDP) transport.
+ *
+ * Requires:
+ *
+ *\li 'client' is a valid client.
+ *
+ *\li 'qmessage' and 'rmessage' are valid initialized message.
+ *
+ *\li 'server' is a valid socket address structure.
+ *
+ *\li 'task' is a valid task.
+ *
+ *\li 'transp' != NULL && *transp == NULL;
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS On success.
+ *
+ *\li Anything else Failure.
+ *
+ *\li Any result that dns_message_parse() can return.
+ */
+
+void
+dns_client_cancelrequest(dns_clientreqtrans_t *transp);
+/*%<
+ * Cancel an ongoing DNS request procedure started via
+ * dns_client_startrequest().
+ *
+ * Notes:
+ *
+ *\li If the request procedure has not completed, post its CLIENTREQDONE
+ * event with a result code of #ISC_R_CANCELED.
+ *
+ * Requires:
+ *
+ *\li 'trans' is a valid transaction ID.
+ */
+
+void
+dns_client_destroyreqtrans(dns_clientreqtrans_t **transp);
+/*%
+ * Destroy DNS request transaction state identified by '*transp'.
+ *
+ * Requires:
+ *
+ *\li '*transp' is a valid transaction ID.
+ *
+ *\li The caller has received the CLIENTREQDONE event (either because the
+ * request completed or because dns_client_cancelrequest() was called).
+ *
+ * Ensures:
+ *
+ *\li *transp == NULL.
+ */
+
+isc_result_t
+dns_client_update(dns_client_t *client, dns_rdataclass_t rdclass,
+ dns_name_t *zonename, dns_namelist_t *prerequisites,
+ dns_namelist_t *updates, isc_sockaddrlist_t *servers,
+ dns_tsec_t *tsec, unsigned int options);
+
+isc_result_t
+dns_client_startupdate(dns_client_t *client, dns_rdataclass_t rdclass,
+ dns_name_t *zonename, dns_namelist_t *prerequisites,
+ dns_namelist_t *updates, isc_sockaddrlist_t *servers,
+ dns_tsec_t *tsec, unsigned int options,
+ isc_task_t *task, isc_taskaction_t action, void *arg,
+ dns_clientupdatetrans_t **transp);
+/*%<
+ * Perform DNS dynamic update for 'updates' of the 'rdclass' class with
+ * optional 'prerequisites'.
+ *
+ * 'updates' are a list of names with associated RRsets to be updated.
+ *
+ * 'prerequisites' are a list of names with associated RRsets corresponding to
+ * the prerequisites of the updates. This is optional and can be NULL, in
+ * which case the prerequisite section of the update message will be empty.
+ *
+ * Both 'updates' and 'prerequisites' must be constructed as specified in
+ * RFC2136.
+ *
+ * 'zonename' is the name of the zone in which the updated names exist.
+ * This is optional and can be NULL. In this case, these functions internally
+ * identify the appropriate zone through some queries for the SOA RR starting
+ * with the first name in prerequisites or updates.
+ *
+ * 'servers' is a list of authoritative servers to which the update message
+ * should be sent. This is optional and can be NULL. In this case, these
+ * functions internally identify the appropriate primary server name and its
+ * addresses through some queries for the SOA RR (like the case of zonename)
+ * and supplemental A/AAAA queries for the server name.
+ * Note: The client module generally assumes the given addresses are of the
+ * primary server of the corresponding zone. It will work even if a secondary
+ * server address is specified as long as the server allows update forwarding,
+ * it is generally discouraged to include secondary server addresses unless
+ * there's strong reason to do so.
+ *
+ * 'tsec' is a transaction security object containing, e.g. a TSIG key for
+ * authenticating the update transaction (and the supplemental query/response
+ * transactions if the server is specified). This is optional and can be
+ * NULL, in which case the library tries the update without any transaction
+ * authentication.
+ *
+ * It is typically expected that the client object passed to
+ * dns_client_update() was created via dns_client_create() and has its own
+ * managers and contexts. However, if the DNS_CLIENTUPDOPT_ALLOWRUN flag is
+ * set in 'options', this function performs the synchronous service even if
+ * it does not have its own manager and context structures.
+ *
+ * dns_client_update() provides a synchronous service. This function blocks
+ * until the entire update procedure completes, including the additional
+ * queries when necessary.
+ *
+ * dns_client_startupdate() is an asynchronous version of dns_client_update().
+ * It immediately returns (typically with *transp being set to a non-NULL
+ * pointer), and performs the update procedure through a set of internal
+ * events. All transactions including the additional query exchanges are
+ * performed as a separate event, so none of these events cause blocking
+ * operation. When the update procedure completes, the specified function
+ * 'action' will be called with the argument of a 'dns_clientupdateevent_t'
+ * structure. On return, '*transp' is set to an opaque transaction ID so that
+ * the caller can cancel this update process.
+ *
+ * DNS_CLIENTUPDOPT_TCP switches to the TCP (vs. UDP) transport.
+ *
+ * Requires:
+ *
+ *\li 'client' is a valid client.
+ *
+ *\li 'updates' != NULL.
+ *
+ *\li 'task' is a valid task.
+ *
+ *\li 'transp' != NULL && *transp == NULL;
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS On success.
+ *
+ *\li Anything else Failure.
+ */
+
+void
+dns_client_cancelupdate(dns_clientupdatetrans_t *trans);
+/*%<
+ * Cancel an ongoing dynamic update procedure started via
+ * dns_client_startupdate().
+ *
+ * Notes:
+ *
+ *\li If the update procedure has not completed, post its UPDATEDONE
+ * event with a result code of #ISC_R_CANCELED.
+ *
+ * Requires:
+ *
+ *\li 'trans' is a valid transaction ID.
+ */
+
+void
+dns_client_destroyupdatetrans(dns_clientupdatetrans_t **transp);
+/*%<
+ * Destroy dynamic update transaction identified by '*transp'.
+ *
+ * Requires:
+ *
+ *\li '*transp' is a valid transaction ID.
+ *
+ *\li The caller has received the UPDATEDONE event (either because the
+ * update completed or because dns_client_cancelupdate() was called).
+ *
+ * Ensures:
+ *
+ *\li *transp == NULL.
+ */
+
+isc_result_t
+dns_client_updaterec(dns_client_updateop_t op, dns_name_t *owner,
+ dns_rdatatype_t type, dns_rdata_t *source,
+ dns_ttl_t ttl, dns_name_t *target,
+ dns_rdataset_t *rdataset, dns_rdatalist_t *rdatalist,
+ dns_rdata_t *rdata, isc_mem_t *mctx);
+/*%<
+ * TBD
+ */
+
+void
+dns_client_freeupdate(dns_name_t **namep);
+/*%<
+ * TBD
+ */
+
+isc_mem_t *
+dns_client_mctx(dns_client_t *client);
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_CLIENT_H */
diff --git a/lib/dns/include/dns/clientinfo.h b/lib/dns/include/dns/clientinfo.h
new file mode 100644
index 0000000..30e29c8
--- /dev/null
+++ b/lib/dns/include/dns/clientinfo.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#ifndef DNS_CLIENTINFO_H
+#define DNS_CLIENTINFO_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file dns/clientinfo.h
+ * \brief
+ * The DNS clientinfo interface allows libdns to retrieve information
+ * about the client from the caller.
+ *
+ * The clientinfo interface is used by the DNS DB and DLZ interfaces;
+ * it allows databases to modify their answers on the basis of information
+ * about the client, such as source IP address.
+ *
+ * dns_clientinfo_t contains a pointer to an opaque structure containing
+ * client information in some form. dns_clientinfomethods_t contains a
+ * list of methods which operate on that opaque structure to return
+ * potentially useful data. Both structures also contain versioning
+ * information.
+ */
+
+/*****
+ ***** Imports
+ *****/
+
+#include <inttypes.h>
+
+#include <isc/sockaddr.h>
+#include <isc/types.h>
+
+ISC_LANG_BEGINDECLS
+
+/*****
+ ***** Types
+ *****/
+
+#define DNS_CLIENTINFO_VERSION 2
+typedef struct dns_clientinfo {
+ uint16_t version;
+ void *data;
+ void *dbversion;
+} dns_clientinfo_t;
+
+typedef isc_result_t (*dns_clientinfo_sourceip_t)(dns_clientinfo_t *client,
+ isc_sockaddr_t **addrp);
+
+#define DNS_CLIENTINFOMETHODS_VERSION 1
+#define DNS_CLIENTINFOMETHODS_AGE 0
+
+typedef struct dns_clientinfomethods {
+ uint16_t version;
+ uint16_t age;
+ dns_clientinfo_sourceip_t sourceip;
+} dns_clientinfomethods_t;
+
+/*****
+ ***** Methods
+ *****/
+void
+dns_clientinfomethods_init(dns_clientinfomethods_t *methods,
+ dns_clientinfo_sourceip_t sourceip);
+
+void
+dns_clientinfo_init(dns_clientinfo_t *ci, void *data, void *versionp);
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_CLIENTINFO_H */
diff --git a/lib/dns/include/dns/compress.h b/lib/dns/include/dns/compress.h
new file mode 100644
index 0000000..0c951d7
--- /dev/null
+++ b/lib/dns/include/dns/compress.h
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef DNS_COMPRESS_H
+#define DNS_COMPRESS_H 1
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/lang.h>
+#include <isc/region.h>
+
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+/*! \file dns/compress.h
+ * Direct manipulation of the structures is strongly discouraged.
+ *
+ * A name compression context handles compression of multiple DNS names
+ * in relation to a single DNS message. The context can be used to
+ * selectively turn on/off compression for specific names (depending on
+ * the RR type) by using \c dns_compress_setmethods(). Alternately,
+ * compression can be disabled completely using \c
+ * dns_compress_disable().
+ *
+ * \c dns_compress_setmethods() is intended for use by RDATA towire()
+ * implementations, whereas \c dns_compress_disable() is intended to be
+ * used by a nameserver's configuration manager.
+ */
+
+#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. */
+#define DNS_COMPRESS_ENABLED 0x04
+
+#define DNS_COMPRESS_READY 0x80000000
+
+#define DNS_COMPRESS_TABLESIZE 64
+#define DNS_COMPRESS_INITIALNODES 16
+
+typedef struct dns_compressnode dns_compressnode_t;
+
+struct dns_compressnode {
+ isc_region_t r;
+ uint16_t offset;
+ uint16_t count;
+ 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];
+ 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);
+/*%<
+ * Initialise the compression context structure pointed to by
+ * 'cctx'. A freshly initialized context has name compression
+ * enabled, but no methods are set. Please use \c
+ * dns_compress_setmethods() to set a compression method.
+ *
+ * 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
+ */
+
+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_disable(dns_compress_t *cctx);
+/*%<
+ * Disables all name compression in the context. Once disabled,
+ * name compression cannot currently be re-enabled.
+ *
+ * Requires:
+ *\li 'cctx' to be initialized.
+ *
+ */
+
+void
+dns_compress_setsensitive(dns_compress_t *cctx, bool sensitive);
+
+/*
+ * Preserve the case of compressed domain names.
+ *
+ * Requires:
+ * 'cctx' to be initialized.
+ */
+
+bool
+dns_compress_getsensitive(dns_compress_t *cctx);
+/*
+ * Return whether case is to be preserved 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
+ */
+
+bool
+dns_compress_findglobal(dns_compress_t *cctx, const dns_name_t *name,
+ dns_name_t *prefix, 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 uint16_t.
+ *
+ * Ensures:
+ *\li 'prefix' and 'offset' are valid if true is returned.
+ *
+ * Returns:
+ *\li #true / #false
+ */
+
+void
+dns_compress_add(dns_compress_t *cctx, const dns_name_t *name,
+ const dns_name_t *prefix, 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, 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..ae6ae36
--- /dev/null
+++ b/lib/dns/include/dns/db.h
@@ -0,0 +1,1690 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $Id$ */
+
+#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 <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/deprecated.h>
+#include <isc/lang.h>
+#include <isc/magic.h>
+#include <isc/ondestroy.h>
+#include <isc/stats.h>
+#include <isc/stdtime.h>
+
+#include <dns/clientinfo.h>
+#include <dns/fixedname.h>
+#include <dns/name.h>
+#include <dns/rdata.h>
+#include <dns/rdataset.h>
+#include <dns/rpz.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_rdatacallbacks_t *callbacks);
+ isc_result_t (*endload)(dns_db_t *db,
+ dns_rdatacallbacks_t *callbacks);
+ isc_result_t (*serialize)(dns_db_t *db,
+ dns_dbversion_t *version, FILE *file);
+ 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,
+ bool commit);
+ isc_result_t (*findnode)(dns_db_t *db, dns_name_t *name,
+ bool 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);
+ bool (*issecure)(dns_db_t *db);
+ unsigned int (*nodecount)(dns_db_t *db);
+ bool (*ispersistent)(dns_db_t *db);
+ void (*overmem)(dns_db_t *db, bool 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,
+ uint8_t *flags,
+ uint16_t *iterations,
+ unsigned char *salt,
+ size_t *salt_len);
+ isc_result_t (*findnsec3node)(dns_db_t *db, dns_name_t *name,
+ bool 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);
+ bool (*isdnssec)(dns_db_t *db);
+ dns_stats_t *(*getrrsetstats)(dns_db_t *db);
+ void (*rpz_attach)(dns_db_t *db, dns_rpz_zones_t *rpzs,
+ dns_rpz_num_t rpz_num);
+ isc_result_t (*rpz_ready)(dns_db_t *db);
+ isc_result_t (*findnodeext)(dns_db_t *db, dns_name_t *name,
+ bool create,
+ dns_clientinfomethods_t *methods,
+ dns_clientinfo_t *clientinfo,
+ dns_dbnode_t **nodep);
+ isc_result_t (*findext)(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_clientinfomethods_t *methods,
+ dns_clientinfo_t *clientinfo,
+ dns_rdataset_t *rdataset,
+ dns_rdataset_t *sigrdataset);
+ isc_result_t (*setcachestats)(dns_db_t *db, isc_stats_t *stats);
+ size_t (*hashsize)(dns_db_t *db);
+ isc_result_t (*nodefullname)(dns_db_t *db, dns_dbnode_t *node,
+ dns_name_t *name);
+ isc_result_t (*getsize)(dns_db_t *db, dns_dbversion_t *version,
+ uint64_t *records, uint64_t *bytes);
+} 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);
+
+typedef isc_result_t
+(*dns_dbupdate_callback_t)(dns_db_t *db, void *fn_arg);
+
+#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;
+ uint16_t attributes;
+ dns_rdataclass_t rdclass;
+ dns_name_t origin;
+ isc_ondestroy_t ondest;
+ isc_mem_t * mctx;
+ ISC_LIST(dns_dbonupdatelistener_t) update_listeners;
+};
+
+#define DNS_DBATTR_CACHE 0x01
+#define DNS_DBATTR_STUB 0x02
+
+struct dns_dbonupdatelistener {
+ dns_dbupdate_callback_t onupdate;
+ void * onupdate_arg;
+ ISC_LINK(dns_dbonupdatelistener_t) link;
+};
+
+/*@{*/
+/*%
+ * Options that can be specified for dns_db_find().
+ */
+#define DNS_DBFIND_GLUEOK 0x0001
+#define DNS_DBFIND_VALIDATEGLUE 0x0002
+#define DNS_DBFIND_NOWILD 0x0004
+#define DNS_DBFIND_PENDINGOK 0x0008
+#define DNS_DBFIND_NOEXACT 0x0010
+#define DNS_DBFIND_FORCENSEC 0x0020
+#define DNS_DBFIND_COVERINGNSEC 0x0040
+#define DNS_DBFIND_FORCENSEC3 0x0080
+#define DNS_DBFIND_ADDITIONALOK 0x0100
+#define DNS_DBFIND_NOZONECUT 0x0200
+/*@}*/
+
+/*@{*/
+/*%
+ * 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
+#define DNS_DBADD_PREFETCH 0x10
+/*@}*/
+
+/*%
+ * Options that can be specified for dns_db_subtractrdataset().
+ */
+#define DNS_DBSUB_EXACT 0x01
+#define DNS_DBSUB_WANTOLD 0x02
+
+/*@{*/
+/*%
+ * 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.
+ */
+
+bool
+dns_db_iscache(dns_db_t *db);
+/*%<
+ * Does 'db' have cache semantics?
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database.
+ *
+ * Returns:
+ * \li #true 'db' has cache semantics
+ * \li #false otherwise
+ */
+
+bool
+dns_db_iszone(dns_db_t *db);
+/*%<
+ * Does 'db' have zone semantics?
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database.
+ *
+ * Returns:
+ * \li #true 'db' has zone semantics
+ * \li #false otherwise
+ */
+
+bool
+dns_db_isstub(dns_db_t *db);
+/*%<
+ * Does 'db' have stub semantics?
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database.
+ *
+ * Returns:
+ * \li #true 'db' has zone semantics
+ * \li #false otherwise
+ */
+
+bool
+dns_db_issecure(dns_db_t *db);
+/*%<
+ * Is 'db' secure?
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database with zone semantics.
+ *
+ * Returns:
+ * \li #true 'db' is secure.
+ * \li #false 'db' is not secure.
+ */
+
+bool
+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 #true 'db' is secure or is partially.
+ * \li #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_rdatacallbacks_t *callbacks);
+/*%<
+ * Begin loading 'db'.
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database.
+ *
+ * \li This is the first attempt to load 'db'.
+ *
+ * \li 'callbacks' is a pointer to an initialized dns_rdatacallbacks_t
+ * structure.
+ *
+ * Ensures:
+ *
+ * \li On success, callbacks->add will be a valid dns_addrdatasetfunc_t
+ * suitable for loading records into 'db' from a raw or text zone
+ * file. callbacks->add_private will be a valid DB load context
+ * which should be used as 'arg' when callbacks->add is called.
+ * callbacks->deserialize will be a valid dns_deserialize_func_t
+ * suitable for loading 'db' from a map format zone file.
+ *
+ * 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_rdatacallbacks_t *callbacks);
+/*%<
+ * Finish loading 'db'.
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database that is being loaded.
+ *
+ * \li 'callbacks' is a valid dns_rdatacallbacks_t structure.
+ *
+ * \li callbacks->add_private is not NULL and is a valid database load context.
+ *
+ * Ensures:
+ *
+ * \li 'callbacks' is returned to its state prior to calling dns_db_beginload()
+ *
+ * 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);
+
+isc_result_t
+dns_db_load3(dns_db_t *db, const char *filename, dns_masterformat_t format,
+ unsigned int options);
+/*%<
+ * 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_serialize(dns_db_t *db, dns_dbversion_t *version, FILE *rbtfile);
+/*%<
+ * Dump version 'version' of 'db' to map-format 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.
+ */
+
+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,
+ bool commit);
+/*%<
+ * Close version '*versionp'.
+ *
+ * Note: if '*versionp' is a read-write version and 'commit' is true,
+ * then all changes made in the version will take effect, otherwise they
+ * will be rolled back. The value of '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 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, bool create,
+ dns_dbnode_t **nodep);
+
+isc_result_t
+dns_db_findnodeext(dns_db_t *db, dns_name_t *name, bool create,
+ dns_clientinfomethods_t *methods,
+ dns_clientinfo_t *clientinfo, dns_dbnode_t **nodep);
+/*%<
+ * Find the node with name 'name'.
+ *
+ * dns_db_findnodeext() (findnode extended) also accepts parameters
+ * 'methods' and 'clientinfo', which, when provided, enable the database to
+ * retreive information about the client from the caller, and modify its
+ * response on the basis of that information.
+ *
+ * Notes:
+ * \li If 'create' is 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 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);
+
+isc_result_t
+dns_db_findext(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_clientinfomethods_t *methods, dns_clientinfo_t *clientinfo,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset);
+/*%<
+ * Find the best match for 'name' and 'type' in version 'version' of 'db'.
+ *
+ * dns_db_findext() (find extended) also accepts parameters 'methods'
+ * and 'clientinfo', which when provided enable the database to retreive
+ * information about the client from the caller, and modify its response
+ * on the basis of this information.
+ *
+ * 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_ADDITIONALOK set, then no
+ * additional records will be returned. Only caches can have
+ * rdataset with trust dns_trust_additional.
+ *
+ * \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_NOZONECUT option is set, the database is
+ * assumed to contain no zone cuts above 'name'. An implementation
+ * may therefore choose to search for a match beginning at 'name'
+ * rather than walking down the tree to check check for delegations.
+ * If #DNS_DBFIND_NOWILD is not set, wildcard matching will be
+ * attempted at each node starting at the direct ancestor of 'name'
+ * and working up to the zone origin. This option is only meaningful
+ * when querying redirect zones.
+ *
+ * \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 If the #DNS_DBFIND_FORCENSEC3 option is set, then we are looking
+ * in the NSEC3 tree and not the main tree. Without this option being
+ * set NSEC3 records will not be found.
+ *
+ * \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_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'.
+ *
+ * \li #DNS_R_EMPTYWILD The name is a wildcard without
+ * resource records.
+ *
+ * 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 iterator 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'.
+ *
+ *\li If DNS_DBSUB_WANTOLD is set and the entire rdataset was deleted
+ * then return the original rdatatset in newrdataset if that existed.
+ *
+ * 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, 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, bool overmem);
+/*%<
+ * Enable / disable aggressive 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
+ */
+
+size_t
+dns_db_hashsize(dns_db_t *db);
+/*%<
+ * For database implementations using a hash table, report the
+ * current number of buckets.
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database.
+ *
+ * Returns:
+ * \li The number of buckets in the database's hash table, or
+ * 0 if not implemented.
+ */
+
+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.
+ */
+
+bool
+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 #true 'db' is persistent.
+ * \li #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 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, uint8_t *flags,
+ 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_getsize(dns_db_t *db, dns_dbversion_t *version, uint64_t *records,
+ uint64_t *bytes);
+/*%<
+ * Get the number of records in the given version of the database as well
+ * as the number bytes used to store those records.
+ *
+ * Requires:
+ * \li 'db' is a valid zone database.
+ * \li 'version' is NULL or a valid version.
+ * \li 'records' is NULL or a pointer to return the record count in.
+ * \li 'bytes' is NULL or a pointer to return the byte count in.
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS
+ * \li #ISC_R_NOTIMPLEMENTED
+ */
+
+isc_result_t
+dns_db_findnsec3node(dns_db_t *db, dns_name_t *name,
+ bool create, dns_dbnode_t **nodep);
+/*%<
+ * Find the NSEC3 node with name 'name'.
+ *
+ * Notes:
+ * \li If 'create' is 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 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' is or is to be associated with 'db'.
+ * \li 'rdataset' is not pending removed from the heap via an
+ * uncommitted call to dns_db_resigned().
+ *
+ * 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. Until
+ * 'version' is either committed or rolled back, 'rdataset' can no longer
+ * be acted upon by dns_db_setsigningtime().
+ *
+ * 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 (cache only).
+ *
+ * Returns:
+ * \li when available, a pointer to a statistics object created by
+ * dns_rdatasetstats_create(); otherwise NULL.
+ */
+
+isc_result_t
+dns_db_setcachestats(dns_db_t *db, isc_stats_t *stats);
+/*%<
+ * Set the location in which to collect cache statistics.
+ * This option may not exist depending on the DB implementation.
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database (cache only).
+ *
+ * Returns:
+ * \li when available, a pointer to a statistics object created by
+ * dns_rdatasetstats_create(); otherwise NULL.
+ */
+
+void
+dns_db_rpz_attach(dns_db_t *db, dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num);
+/*%<
+ * Attach the response policy information for a view to a database for a
+ * zone for the view.
+ */
+
+isc_result_t
+dns_db_rpz_ready(dns_db_t *db);
+/*%<
+ * Finish loading a response policy zone.
+ */
+
+isc_result_t
+dns_db_updatenotify_register(dns_db_t *db,
+ dns_dbupdate_callback_t fn,
+ void *fn_arg);
+/*%<
+ * Register a notify-on-update callback function to a database.
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database
+ * \li 'db' does not have an update callback registered
+ * \li 'fn' is not NULL
+ *
+ */
+
+isc_result_t
+dns_db_updatenotify_unregister(dns_db_t *db,
+ dns_dbupdate_callback_t fn,
+ void *fn_arg);
+/*%<
+ * Unregister a notify-on-update callback.
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database
+ * \li 'db' has update callback registered
+ *
+ */
+
+isc_result_t
+dns_db_nodefullname(dns_db_t *db, dns_dbnode_t *node, dns_name_t *name);
+/*%<
+ * Get the name associated with a database node.
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database
+ * \li 'node' and 'name' are not 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..8b50c8c
--- /dev/null
+++ b/lib/dns/include/dns/dbiterator.h
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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 <stdbool.h>
+
+#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;
+ bool relative_names;
+ bool 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 #DNS_R_PARTIALMATCH
+ * (node is at name above requested named when name has children)
+ *
+ *\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 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, bool mode);
+/*%<
+ * Indicate that the given iterator is/is not cleaning the DB.
+ *
+ * Notes:
+ *\li When 'mode' is 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..8f139b9
--- /dev/null
+++ b/lib/dns/include/dns/dbtable.h
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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..50f6e9d
--- /dev/null
+++ b/lib/dns/include/dns/diff.h
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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;
+ ISC_LIST(dns_difftuple_t) tuples;
+};
+
+/* Type of comparison function for sorting diffs. */
+typedef int dns_diff_compare_func(const void *, const void *);
+
+/***
+ *** Functions
+ ***/
+
+ISC_LANG_BEGINDECLS
+
+/**************************************************************************/
+/*
+ * Manipulation 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..476863a
--- /dev/null
+++ b/lib/dns/include/dns/dispatch.h
@@ -0,0 +1,614 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#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 <stdbool.h>
+
+#include <isc/buffer.h>
+#include <isc/lang.h>
+#include <isc/mutex.h>
+#include <isc/socket.h>
+#include <isc/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 */
+ 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 */
+ uint32_t attributes; /*%< mirrored from socket.h */
+};
+
+/*%
+ * This is a set of one or more dispatches which can be retrieved
+ * round-robin fashion.
+ */
+struct dns_dispatchset {
+ isc_mem_t *mctx;
+ dns_dispatch_t **dispatches;
+ int ndisp;
+ int cur;
+ isc_mutex_t lock;
+};
+
+/*@{*/
+/*%
+ * 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_FIXEDID 0x00000100U
+#define DNS_DISPATCHATTR_EXCLUSIVE 0x00000200U
+/*@}*/
+
+/*
+ */
+#define DNS_DISPATCHOPT_FIXEDID 0x00000001U
+
+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, isc_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);
+
+isc_result_t
+dns_dispatch_getudp_dup(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, dns_dispatch_t *dup);
+/*%<
+ * 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);
+isc_result_t
+dns_dispatch_createtcp2(dns_dispatchmgr_t *mgr, isc_socket_t *sock,
+ isc_taskmgr_t *taskmgr, isc_sockaddr_t *localaddr,
+ isc_sockaddr_t *destaddr, 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_gettcp(dns_dispatchmgr_t *mgr, isc_sockaddr_t *destaddr,
+ isc_sockaddr_t *localaddr, dns_dispatch_t **dispp);
+isc_result_t
+dns_dispatch_gettcp2(dns_dispatchmgr_t *mgr, isc_sockaddr_t *destaddr,
+ isc_sockaddr_t *localaddr, bool *connected,
+ dns_dispatch_t **dispp);
+/*
+ * Attempt to connect to a existing TCP connection (connection completed
+ * for dns_dispatch_gettcp()).
+ */
+
+
+isc_result_t
+dns_dispatch_addresponse3(dns_dispatch_t *disp, unsigned int options,
+ isc_sockaddr_t *dest, isc_task_t *task,
+ isc_taskaction_t action, void *arg,
+ uint16_t *idp, dns_dispentry_t **resp,
+ isc_socketmgr_t *sockmgr);
+
+isc_result_t
+dns_dispatch_addresponse2(dns_dispatch_t *disp, isc_sockaddr_t *dest,
+ isc_task_t *task, isc_taskaction_t action, void *arg,
+ 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,
+ 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.
+ *
+ * If the attribute DNS_DISPATCHATTR_NOLISTEN is not set, then
+ * the dispatch is already handling a recv; return immediately.
+ *
+ * Requires:
+ *\li disp is valid, and the attribute DNS_DISPATCHATTR_NOLISTEN is set.
+ * event != NULL
+ */
+
+dns_dispatch_t *
+dns_dispatchset_get(dns_dispatchset_t *dset);
+/*%<
+ * Retrieve the next dispatch from dispatch set 'dset', and increment
+ * the round-robin counter.
+ *
+ * Requires:
+ *\li dset != NULL
+ */
+
+isc_result_t
+dns_dispatchset_create(isc_mem_t *mctx, isc_socketmgr_t *sockmgr,
+ isc_taskmgr_t *taskmgr, dns_dispatch_t *source,
+ dns_dispatchset_t **dsetp, int n);
+/*%<
+ * Given a valid dispatch 'source', create a dispatch set containing
+ * 'n' UDP dispatches, with the remainder filled out by clones of the
+ * source.
+ *
+ * Requires:
+ *\li source is a valid UDP dispatcher
+ *\li dsetp != NULL, *dsetp == NULL
+ */
+
+void
+dns_dispatchset_cancelall(dns_dispatchset_t *dset, isc_task_t *task);
+/*%<
+ * Cancel socket operations for the dispatches in 'dset'.
+ */
+
+void
+dns_dispatchset_destroy(dns_dispatchset_t **dsetp);
+/*%<
+ * Dereference all the dispatches in '*dsetp', free the dispatchset
+ * memory, and set *dsetp to NULL.
+ *
+ * Requires:
+ *\li dset is valid
+ */
+
+void
+dns_dispatch_setdscp(dns_dispatch_t *disp, isc_dscp_t dscp);
+isc_dscp_t
+dns_dispatch_getdscp(dns_dispatch_t *disp);
+/*%<
+ * Set/get the DSCP value to be used when sending responses to clients,
+ * as defined in the "listen-on" or "listen-on-v6" statements.
+ *
+ * Requires:
+ *\li disp is valid.
+ */
+
+isc_result_t
+dns_dispatch_getnext(dns_dispentry_t *resp, dns_dispatchevent_t **sockevent);
+/*%<
+ * Free the sockevent and trigger the sending of the next item off the
+ * dispatch queue if present.
+ *
+ * Requires:
+ *\li resp is valid
+ *\li *sockevent to be valid
+ */
+
+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..cc43f7c
--- /dev/null
+++ b/lib/dns/include/dns/dlz.h
@@ -0,0 +1,339 @@
+/*
+ * Portions Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * 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.
+ */
+
+
+/*! \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 <stdbool.h>
+
+#include <dns/clientinfo.h>
+#include <dns/name.h>
+#include <dns/types.h>
+#include <dns/view.h>
+#include <dst/dst.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 shutting 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_clientinfomethods_t *methods,
+ dns_clientinfo_t *clientinfo,
+ 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_labels 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.
+ */
+
+
+typedef isc_result_t
+(*dns_dlzconfigure_t)(void *driverarg, void *dbdata,
+ dns_view_t *view, dns_dlzdb_t *dlzdb);
+/*%<
+ * Method prototype. Drivers implementing the DLZ interface may
+ * optionally supply a configure method. If supplied, this will be
+ * called immediately after the create method is called. The driver
+ * may call configuration functions during the configure call
+ */
+
+
+typedef bool (*dns_dlzssumatch_t)(dns_name_t *signer,
+ dns_name_t *name,
+ isc_netaddr_t *tcpaddr,
+ dns_rdatatype_t type,
+ const dst_key_t *key,
+ void *driverarg, void *dbdata);
+/*%<
+ * Method prototype. Drivers implementing the DLZ interface may
+ * optionally supply a ssumatch method. If supplied, this will be
+ * called to authorize update requests
+ */
+
+/*% 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_dlzconfigure_t configure;
+ dns_dlzssumatch_t ssumatch;
+} 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;
+};
+
+typedef isc_result_t (*dlzconfigure_callback_t)(dns_view_t *, dns_dlzdb_t *,
+ dns_zone_t *);
+
+/*% An instance of a DLZ driver */
+struct dns_dlzdb {
+ unsigned int magic;
+ isc_mem_t *mctx;
+ dns_dlzimplementation_t *implementation;
+ void *dbdata;
+ dlzconfigure_callback_t configure_callback;
+ bool search;
+ char *dlzname;
+ ISC_LINK(dns_dlzdb_t) link;
+ dns_ssutable_t *ssutable;
+};
+
+
+/***
+ *** 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 transfer
+ * 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 shutting down and no
+ * longer needs the driver. If the DLZ driver supplies a destroy
+ * methods, this function will call it.
+ */
+
+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.
+ */
+
+
+typedef isc_result_t dns_dlz_writeablezone_t(dns_view_t *view,
+ dns_dlzdb_t *dlzdb,
+ const char *zone_name);
+dns_dlz_writeablezone_t dns_dlz_writeablezone;
+/*%<
+ * creates a writeable DLZ zone. Must be called from within the
+ * configure() method of a DLZ driver.
+ */
+
+
+isc_result_t
+dns_dlzconfigure(dns_view_t *view, dns_dlzdb_t *dlzdb,
+ dlzconfigure_callback_t callback);
+/*%<
+ * call a DLZ drivers configure method, if supplied
+ */
+
+bool
+dns_dlz_ssumatch(dns_dlzdb_t *dlzdatabase,
+ dns_name_t *signer, dns_name_t *name, isc_netaddr_t *tcpaddr,
+ dns_rdatatype_t type, const dst_key_t *key);
+/*%<
+ * call a DLZ drivers ssumatch method, if supplied. Otherwise return false
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DLZ_H */
diff --git a/lib/dns/include/dns/dlz_dlopen.h b/lib/dns/include/dns/dlz_dlopen.h
new file mode 100644
index 0000000..3719aa7
--- /dev/null
+++ b/lib/dns/include/dns/dlz_dlopen.h
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file dns/dlz_dlopen.h */
+
+#ifndef DLZ_DLOPEN_H
+#define DLZ_DLOPEN_H
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <dns/sdlz.h>
+
+ISC_LANG_BEGINDECLS
+
+/*
+ * This header provides a minimal set of defines and typedefs needed
+ * for the entry points of an external DLZ module for bind9.
+ */
+
+#define DLZ_DLOPEN_VERSION 3
+#define DLZ_DLOPEN_AGE 0
+
+/*
+ * dlz_dlopen_version() is required for all DLZ external drivers. It
+ * should return DLZ_DLOPEN_VERSION
+ */
+typedef int dlz_dlopen_version_t(unsigned int *flags);
+
+/*
+ * dlz_dlopen_create() is required for all DLZ external drivers.
+ */
+typedef isc_result_t dlz_dlopen_create_t(const char *dlzname,
+ unsigned int argc,
+ char *argv[],
+ void **dbdata,
+ ...);
+
+/*
+ * dlz_dlopen_destroy() is optional, and will be called when the
+ * driver is unloaded if supplied
+ */
+typedef void dlz_dlopen_destroy_t(void *dbdata);
+
+/*
+ * dlz_dlopen_findzonedb() is required for all DLZ external drivers
+ */
+typedef isc_result_t dlz_dlopen_findzonedb_t(void *dbdata,
+ const char *name,
+ dns_clientinfomethods_t *methods,
+ dns_clientinfo_t *clientinfo);
+
+/*
+ * dlz_dlopen_lookup() is required for all DLZ external drivers
+ */
+typedef isc_result_t dlz_dlopen_lookup_t(const char *zone,
+ const char *name,
+ void *dbdata,
+ dns_sdlzlookup_t *lookup,
+ dns_clientinfomethods_t *methods,
+ dns_clientinfo_t *clientinfo);
+
+/*
+ * dlz_dlopen_authority is optional() if dlz_dlopen_lookup()
+ * supplies authority information for the dns record
+ */
+typedef isc_result_t dlz_dlopen_authority_t(const char *zone,
+ void *dbdata,
+ dns_sdlzlookup_t *lookup);
+
+/*
+ * dlz_dlopen_allowzonexfr() is optional, and should be supplied if
+ * you want to support zone transfers
+ */
+typedef isc_result_t dlz_dlopen_allowzonexfr_t(void *dbdata,
+ const char *name,
+ const char *client);
+
+/*
+ * dlz_dlopen_allnodes() is optional, but must be supplied if supply a
+ * dlz_dlopen_allowzonexfr() function
+ */
+typedef isc_result_t dlz_dlopen_allnodes_t(const char *zone,
+ void *dbdata,
+ dns_sdlzallnodes_t *allnodes);
+
+/*
+ * dlz_dlopen_newversion() is optional. It should be supplied if you
+ * want to support dynamic updates.
+ */
+typedef isc_result_t dlz_dlopen_newversion_t(const char *zone,
+ void *dbdata,
+ void **versionp);
+
+/*
+ * dlz_closeversion() is optional, but must be supplied if you supply
+ * a dlz_newversion() function
+ */
+typedef void dlz_dlopen_closeversion_t(const char *zone,
+ bool commit,
+ void *dbdata,
+ void **versionp);
+
+/*
+ * dlz_dlopen_configure() is optional, but must be supplied if you
+ * want to support dynamic updates
+ */
+typedef isc_result_t dlz_dlopen_configure_t(dns_view_t *view,
+ dns_dlzdb_t *dlzdb,
+ void *dbdata);
+
+/*
+ * dlz_dlopen_setclientcallback() is optional, but must be supplied if you
+ * want to retrieve information about the client (e.g., source address)
+ * before sending a replay.
+ */
+typedef isc_result_t dlz_dlopen_setclientcallback_t(dns_view_t *view,
+ void *dbdata);
+
+
+/*
+ * dlz_dlopen_ssumatch() is optional, but must be supplied if you want
+ * to support dynamic updates
+ */
+typedef bool dlz_dlopen_ssumatch_t(const char *signer,
+ const char *name,
+ const char *tcpaddr,
+ const char *type,
+ const char *key,
+ uint32_t keydatalen,
+ unsigned char *keydata,
+ void *dbdata);
+
+/*
+ * dlz_dlopen_addrdataset() is optional, but must be supplied if you
+ * want to support dynamic updates
+ */
+typedef isc_result_t dlz_dlopen_addrdataset_t(const char *name,
+ const char *rdatastr,
+ void *dbdata,
+ void *version);
+
+/*
+ * dlz_dlopen_subrdataset() is optional, but must be supplied if you
+ * want to support dynamic updates
+ */
+typedef isc_result_t dlz_dlopen_subrdataset_t(const char *name,
+ const char *rdatastr,
+ void *dbdata,
+ void *version);
+
+/*
+ * dlz_dlopen_delrdataset() is optional, but must be supplied if you
+ * want to support dynamic updates
+ */
+typedef isc_result_t dlz_dlopen_delrdataset_t(const char *name,
+ const char *type,
+ void *dbdata,
+ void *version);
+
+ISC_LANG_ENDDECLS
+
+#endif
diff --git a/lib/dns/include/dns/dns64.h b/lib/dns/include/dns/dns64.h
new file mode 100644
index 0000000..34c2ad5
--- /dev/null
+++ b/lib/dns/include/dns/dns64.h
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#ifndef DNS_DNS64_H
+#define DNS_DNS64_H 1
+
+#include <stdbool.h>
+
+#include <isc/lang.h>
+
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+/*
+ * dns_dns64_create() flags.
+ */
+#define DNS_DNS64_RECURSIVE_ONLY 0x01 /* If set then this record
+ * only applies to recursive
+ * queries.
+ */
+#define DNS_DNS64_BREAK_DNSSEC 0x02 /* If set then still perform
+ * DNSSEC synthesis even
+ * though the result would
+ * fail validation.
+ */
+
+/*
+ * dns_dns64_aaaaok() and dns_dns64_aaaafroma() flags.
+ */
+#define DNS_DNS64_RECURSIVE 0x01 /* Recursive query. */
+#define DNS_DNS64_DNSSEC 0x02 /* DNSSEC sensitive query. */
+
+isc_result_t
+dns_dns64_create(isc_mem_t *mctx, isc_netaddr_t *prefix,
+ unsigned int prefixlen, isc_netaddr_t *suffix,
+ dns_acl_t *client, dns_acl_t *mapped, dns_acl_t *excluded,
+ unsigned int flags, dns_dns64_t **dns64);
+/*
+ * Create a dns64 record which is used to identify the set of clients
+ * it applies to and how to perform the DNS64 synthesis.
+ *
+ * 'prefix' and 'prefixlen' defined the leading bits of the AAAA records
+ * to be synthesised. 'suffix' defines the bits after the A records bits.
+ * If suffix is NULL zeros will be used for these bits. 'client' defines
+ * for which clients this record applies. If 'client' is NULL then all
+ * clients apply. 'mapped' defines which A records are candidated for
+ * mapping. If 'mapped' is NULL then all A records will be mapped.
+ * 'excluded' defines which AAAA are to be treated as non-existent for the
+ * purposed of determining whether to perform syntesis. If 'excluded' is
+ * NULL then no AAAA records prevent synthesis.
+ *
+ * If DNS_DNS64_RECURSIVE_ONLY is set then the record will only match if
+ * DNS_DNS64_RECURSIVE is set when calling dns_dns64_aaaaok() and
+ * dns_dns64_aaaafroma().
+ *
+ * If DNS_DNS64_BREAK_DNSSEC is set then the record will still apply if
+ * DNS_DNS64_DNSSEC is set when calling dns_dns64_aaaaok() and
+ * dns_dns64_aaaafroma() otherwise the record will be ignored.
+ *
+ * Requires:
+ * 'mctx' to be valid.
+ * 'prefix' to be valid and the address family to AF_INET6.
+ * 'prefixlen' to be one of 32, 40, 48, 56, 72 and 96.
+ * the bits not covered by prefixlen in prefix to
+ * be zero.
+ * 'suffix' to be NULL or the address family be set to AF_INET6
+ * and the leading 'prefixlen' + 32 bits of the 'suffix'
+ * to be zero. If 'prefixlen' is 40, 48 or 56 then the
+ * the leading 'prefixlen' + 40 bits of 'suffix' must be
+ * zero.
+ * 'client' to be NULL or a valid acl.
+ * 'mapped' to be NULL or a valid acl.
+ * 'excluded' to be NULL or a valid acl.
+ *
+ * Returns:
+ * ISC_R_SUCCESS
+ * ISC_R_NOMEMORY
+ */
+
+void
+dns_dns64_destroy(dns_dns64_t **dns64p);
+/*
+ * Destroys a dns64 record.
+ *
+ * Requires the record to not be linked.
+ */
+
+isc_result_t
+dns_dns64_aaaafroma(const dns_dns64_t *dns64, const isc_netaddr_t *reqaddr,
+ const dns_name_t *reqsigner, const dns_aclenv_t *env,
+ unsigned int flags, unsigned char *a, unsigned char *aaaa);
+/*
+ * dns_dns64_aaaafroma() determines whether to perform a DNS64 address
+ * synthesis from 'a' based on 'dns64', 'reqaddr', 'reqsigner', 'env',
+ * 'flags' and 'aaaa'. If synthesis is performed then the result is
+ * written to '*aaaa'.
+ *
+ * The synthesised address will be of the form:
+ *
+ * <prefix bits><a bits><suffix bits>
+ *
+ * If <a bits> straddle bits 64-71 of the AAAA record, then 8 zero bits will
+ * be inserted at bits 64-71.
+ *
+ * Requires:
+ * 'dns64' to be valid.
+ * 'reqaddr' to be valid.
+ * 'reqsigner' to be NULL or valid.
+ * 'env' to be valid.
+ * 'a' to point to a IPv4 address in network order.
+ * 'aaaa' to point to a IPv6 address buffer in network order.
+ *
+ * Returns:
+ * ISC_R_SUCCESS if synthesis was performed.
+ * DNS_R_DISALLOWED if there is no match.
+ */
+
+dns_dns64_t *
+dns_dns64_next(dns_dns64_t *dns64);
+/*
+ * Return the next dns64 record in the list.
+ */
+
+void
+dns_dns64_append(dns_dns64list_t *list, dns_dns64_t *dns64);
+/*
+ * Append the dns64 record to the list.
+ */
+
+void
+dns_dns64_unlink(dns_dns64list_t *list, dns_dns64_t *dns64);
+/*
+ * Unlink the dns64 record from the list.
+ */
+
+bool
+dns_dns64_aaaaok(const dns_dns64_t *dns64, const isc_netaddr_t *reqaddr,
+ const dns_name_t *reqsigner, const dns_aclenv_t *env,
+ unsigned int flags, dns_rdataset_t *rdataset,
+ bool *aaaaok, size_t aaaaoklen);
+/*
+ * Determine if there are any non-excluded AAAA records in from the
+ * matching dns64 records in the list starting at 'dns64'. If there
+ * is a non-exluded address return true. If all addresses are
+ * excluded in the matched records return false. If no records
+ * match then return true.
+ *
+ * If aaaaok is defined then dns_dns64_aaaaok() return a array of which
+ * addresses in 'rdataset' were deemed to not be exclude by any matching
+ * record. If there are no matching records then all entries are set
+ * to true.
+ *
+ * Requires
+ * 'rdataset' to be valid and to be for type AAAA and class IN.
+ * 'aaaaoklen' must match the number of records in 'rdataset'
+ * if 'aaaaok' in non NULL.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_DNS64_H */
diff --git a/lib/dns/include/dns/dnssec.h b/lib/dns/include/dns/dnssec.h
new file mode 100644
index 0000000..0b6369c
--- /dev/null
+++ b/lib/dns/include/dns/dnssec.h
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#ifndef DNS_DNSSEC_H
+#define DNS_DNSSEC_H 1
+
+/*! \file dns/dnssec.h */
+
+#include <stdbool.h>
+
+#include <isc/lang.h>
+#include <isc/stdtime.h>
+#include <isc/stats.h>
+
+#include <dns/diff.h>
+#include <dns/types.h>
+
+#include <dst/dst.h>
+
+ISC_LANG_BEGINDECLS
+
+LIBDNS_EXTERNAL_DATA extern isc_stats_t *dns_dnssec_stats;
+
+/*%< Maximum number of keys supported in a zone. */
+#define DNS_MAXZONEKEYS 32
+
+/*
+ * Indicates how the signer found this key: in the key repository, at the
+ * zone apex, or specified by the user.
+ */
+typedef enum {
+ dns_keysource_unknown,
+ dns_keysource_repository,
+ dns_keysource_zoneapex,
+ dns_keysource_user
+} dns_keysource_t;
+
+/*
+ * A DNSSEC key and hints about its intended use gleaned from metadata
+ */
+struct dns_dnsseckey {
+ dst_key_t *key;
+ bool hint_publish; /*% metadata says to publish */
+ bool force_publish; /*% publish regardless of metadata */
+ bool hint_sign; /*% metadata says to sign with this key */
+ bool force_sign; /*% sign with key regardless of metadata */
+ bool hint_remove; /*% metadata says *don't* publish */
+ bool is_active; /*% key is already active */
+ bool first_sign; /*% key is newly becoming active */
+ unsigned int prepublish; /*% how long until active? */
+ dns_keysource_t source; /*% how the key was found */
+ bool ksk; /*% this is a key-signing key */
+ bool legacy; /*% this is old-style key with no
+ metadata (possibly generated by
+ an older version of BIND9) and
+ should be ignored when searching
+ for keys to import into the zone */
+ unsigned int index; /*% position in list */
+ ISC_LINK(dns_dnsseckey_t) link;
+};
+
+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 RRSIG record covering this rdataset. This has no effect
+ * on existing RRSIG 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,
+ bool 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,
+ bool ignoretime, isc_mem_t *mctx,
+ dns_rdata_t *sigrdata, dns_name_t *wild);
+
+isc_result_t
+dns_dnssec_verify3(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
+ bool ignoretime, unsigned int maxbits,
+ isc_mem_t *mctx, dns_rdata_t *sigrdata, dns_name_t *wild);
+/*%<
+ * Verifies the RRSIG 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 true, temporal validity will not be checked.
+ *
+ * 'maxbits' specifies the maximum number of rsa exponent bits accepted.
+ *
+ * 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);
+
+isc_result_t
+dns_dnssec_findzonekeys3(dns_db_t *db, dns_dbversion_t *ver,
+ dns_dbnode_t *node, dns_name_t *name,
+ const char *directory, isc_stdtime_t now,
+ 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.
+ */
+/*@}*/
+
+bool
+dns_dnssec_keyactive(dst_key_t *key, isc_stdtime_t now);
+/*%<
+ *
+ * Returns true if 'key' is active as of the time specified
+ * in 'now' (i.e., if the activation date has passed, inactivation or
+ * deletion date has not yet been reached, and the key is not revoked
+ * -- or if it is a legacy key without metadata). Otherwise returns
+ * false.
+ *
+ * Requires:
+ *\li 'key' is a valid key
+ */
+
+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_*
+ */
+
+bool
+dns_dnssec_selfsigns(dns_rdata_t *rdata, dns_name_t *name,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset,
+ bool ignoretime, isc_mem_t *mctx);
+
+
+bool
+dns_dnssec_signs(dns_rdata_t *rdata, dns_name_t *name,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset,
+ bool ignoretime, isc_mem_t *mctx);
+/*%<
+ * Verify that 'rdataset' is validly signed in 'sigrdataset' by
+ * the key in 'rdata'.
+ *
+ * dns_dnssec_selfsigns() requires that rdataset be a DNSKEY or KEY
+ * rrset. dns_dnssec_signs() works on any rrset.
+ */
+
+
+isc_result_t
+dns_dnsseckey_create(isc_mem_t *mctx, dst_key_t **dstkey,
+ dns_dnsseckey_t **dkp);
+/*%<
+ * Create and initialize a dns_dnsseckey_t structure.
+ *
+ * Requires:
+ *\li 'dkp' is not NULL and '*dkp' is NULL.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ */
+
+void
+dns_dnsseckey_destroy(isc_mem_t *mctx, dns_dnsseckey_t **dkp);
+/*%<
+ * Reclaim a dns_dnsseckey_t structure.
+ *
+ * Requires:
+ *\li 'dkp' is not NULL and '*dkp' is not NULL.
+ *
+ * Ensures:
+ *\li '*dkp' is NULL.
+ */
+
+isc_result_t
+dns_dnssec_findmatchingkeys(dns_name_t *origin, const char *directory,
+ isc_mem_t *mctx, dns_dnsseckeylist_t *keylist);
+
+isc_result_t
+dns_dnssec_findmatchingkeys2(dns_name_t *origin, const char *directory,
+ isc_stdtime_t now, isc_mem_t *mctx,
+ dns_dnsseckeylist_t *keylist);
+/*%<
+ * Search 'directory' for K* key files matching the name in 'origin'.
+ * Append all such keys, along with use hints gleaned from their
+ * metadata, onto 'keylist'.
+ *
+ * Requires:
+ *\li 'keylist' is not NULL
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOTFOUND
+ *\li #ISC_R_NOMEMORY
+ *\li any error returned by dns_name_totext(), isc_dir_open(), or
+ * dst_key_fromnamedfile()
+ *
+ * Ensures:
+ *\li On error, keylist is unchanged
+ */
+
+isc_result_t
+dns_dnssec_keylistfromrdataset(dns_name_t *origin,
+ const char *directory, isc_mem_t *mctx,
+ dns_rdataset_t *keyset, dns_rdataset_t *keysigs,
+ dns_rdataset_t *soasigs, bool savekeys,
+ bool publickey,
+ dns_dnsseckeylist_t *keylist);
+/*%<
+ * Append the contents of a DNSKEY rdataset 'keyset' to 'keylist'.
+ * Omit duplicates. If 'publickey' is false, search 'directory' for
+ * matching key files, and load the private keys that go with
+ * the public ones. If 'savekeys' is true, mark the keys so
+ * they will not be deleted or inactivated regardless of metadata.
+ *
+ * 'keysigs' and 'soasigs', if not NULL and associated, contain the
+ * RRSIGS for the DNSKEY and SOA records respectively and are used to mark
+ * whether a key is already active in the zone.
+ */
+
+isc_result_t
+dns_dnssec_updatekeys(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *newkeys,
+ dns_dnsseckeylist_t *removed, dns_name_t *origin,
+ dns_ttl_t hint_ttl, dns_diff_t *diff, bool allzsk,
+ isc_mem_t *mctx, void (*report)(const char *, ...));
+/*%<
+ * Update the list of keys in 'keys' with new key information in 'newkeys'.
+ *
+ * For each key in 'newkeys', see if it has a match in 'keys'.
+ * - If not, and if the metadata says the key should be published:
+ * add it to 'keys', and place a dns_difftuple into 'diff' so
+ * the key can be added to the DNSKEY set. If the metadata says it
+ * should be active, set the first_sign flag.
+ * - If so, and if the metadata says it should be removed:
+ * remove it from 'keys', and place a dns_difftuple into 'diff' so
+ * the key can be removed from the DNSKEY set. if 'removed' is non-NULL,
+ * copy the key into that list; otherwise destroy it.
+ * - Otherwise, make sure keys has current metadata.
+ *
+ * If 'allzsk' is true, we are allowing KSK-flagged keys to be used as
+ * ZSKs.
+ *
+ * 'hint_ttl' is the TTL to use for the DNSKEY RRset if there is no
+ * existing RRset, and if none of the keys to be added has a default TTL
+ * (in which case we would use the shortest one). If the TTL is longer
+ * than the time until a new key will be activated, then we have to delay
+ * the key's activation.
+ *
+ * 'report' points to a function for reporting status.
+ *
+ * On completion, any remaining keys in 'newkeys' are freed.
+ */
+
+isc_result_t
+dns_dnssec_syncupdate(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *rmkeys,
+ dns_rdataset_t *cds, dns_rdataset_t *cdnskey,
+ isc_stdtime_t now, dns_ttl_t hint_ttl, dns_diff_t *diff,
+ isc_mem_t *mctx);
+/*%<
+ * Update the CDS and CDNSKEY RRsets, adding and removing keys as needed.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_DNSSEC_H */
diff --git a/lib/dns/include/dns/dnstap.h b/lib/dns/include/dns/dnstap.h
new file mode 100644
index 0000000..279f7fd
--- /dev/null
+++ b/lib/dns/include/dns/dnstap.h
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef _DNSTAP_H
+#define _DNSTAP_H
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file
+ * \brief
+ * The dt (dnstap) module provides fast passive logging of DNS messages.
+ * Protocol Buffers. The protobuf schema for Dnstap messages is in the
+ * file dnstap.proto, which is compiled to dnstap.pb-c.c and dnstap.pb-c.h.
+ */
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#ifdef HAVE_DNSTAP
+#include <fstrm.h>
+#include <protobuf-c/protobuf-c.h>
+#include <dns/dnstap.pb-c.h>
+#else
+struct fstrm_iothr_options;
+#endif /* HAVE_DNSTAP */
+
+#include <isc/region.h>
+#include <isc/sockaddr.h>
+#include <isc/time.h>
+#include <isc/types.h>
+
+#include <dns/name.h>
+#include <dns/rdataclass.h>
+#include <dns/rdatatype.h>
+#include <dns/types.h>
+
+/*%
+ * Dnstap message types:
+ *
+ * STUB QUERY: SQ
+ * STUB RESPONSE: SR
+ * CLIENT QUERY: CQ
+ * CLIENT RESPONSE: CR
+ * AUTH QUERY: AQ
+ * AUTH RESPONSE: AR
+ * RESOLVER QUERY: RQ
+ * RESOLVER RESPONSE: RR
+ * FORWARDER QUERY: FQ
+ * FORWARDER RESPONSE: FR
+ */
+
+#define DNS_DTTYPE_SQ 0x0001
+#define DNS_DTTYPE_SR 0x0002
+#define DNS_DTTYPE_CQ 0x0004
+#define DNS_DTTYPE_CR 0x0008
+#define DNS_DTTYPE_AQ 0x0010
+#define DNS_DTTYPE_AR 0x0020
+#define DNS_DTTYPE_RQ 0x0040
+#define DNS_DTTYPE_RR 0x0080
+#define DNS_DTTYPE_FQ 0x0100
+#define DNS_DTTYPE_FR 0x0200
+#define DNS_DTTYPE_TQ 0x0400
+#define DNS_DTTYPE_TR 0x0800
+
+#define DNS_DTTYPE_QUERY \
+ (DNS_DTTYPE_SQ|DNS_DTTYPE_CQ|DNS_DTTYPE_AQ|\
+ DNS_DTTYPE_RQ|DNS_DTTYPE_FQ|DNS_DTTYPE_TQ)
+#define DNS_DTTYPE_RESPONSE \
+ (DNS_DTTYPE_SR|DNS_DTTYPE_CR|DNS_DTTYPE_AR|\
+ DNS_DTTYPE_RR|DNS_DTTYPE_FR|DNS_DTTYPE_TR)
+#define DNS_DTTYPE_ALL \
+ (DNS_DTTYPE_QUERY|DNS_DTTYPE_RESPONSE)
+
+typedef enum {
+ dns_dtmode_none = 0,
+ dns_dtmode_file,
+ dns_dtmode_unix
+} dns_dtmode_t;
+
+typedef struct dns_dthandle dns_dthandle_t;
+
+#ifdef HAVE_DNSTAP
+struct dns_dtdata {
+ isc_mem_t *mctx;
+
+ Dnstap__Dnstap *frame;
+
+ bool query;
+ bool tcp;
+ dns_dtmsgtype_t type;
+
+ isc_time_t qtime;
+ isc_time_t rtime;
+
+ isc_region_t qaddr;
+ isc_region_t raddr;
+
+ uint32_t qport;
+ uint32_t rport;
+
+ isc_region_t msgdata;
+ dns_message_t *msg;
+
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char typebuf[DNS_RDATATYPE_FORMATSIZE];
+ char classbuf[DNS_RDATACLASS_FORMATSIZE];
+};
+#endif /* HAVE_DNSTAP */
+
+isc_result_t
+dns_dt_create(isc_mem_t *mctx, dns_dtmode_t mode, const char *path,
+ struct fstrm_iothr_options **foptp, dns_dtenv_t **envp);
+/*%<
+ * Create and initialize the dnstap environment.
+ *
+ * There should be a single global dnstap environment for the server;
+ * copies of it will be attached to each view.
+ *
+ * Notes:
+ *
+ *\li 'path' refers to a UNIX domain socket by default. It may
+ * optionally be prepended with "socket:" or "file:". If prepended
+ * with "file:", then dnstap logs are sent to a file instead of a
+ * socket.
+ *
+ *\li '*foptp' set the options for fstrm_iothr_init(). '*foptp' must have
+ * have had the number of input queues set and this should be set
+ * to the number of worker threads. Additionally the queue model
+ * should also be set. Other options may be set if desired.
+ * If dns_dt_create succeeds the *foptp is set to NULL.
+ *
+ * Requires:
+ *
+ *\li 'mctx' is a valid memory context.
+ *
+ *\li 'path' is a valid C string.
+ *
+ *\li 'fopt' is non NULL.
+ *
+ *\li envp != NULL && *envp == NULL
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ *
+ *\li Other errors are possible.
+ */
+
+isc_result_t
+dns_dt_reopen(dns_dtenv_t *env, int roll);
+/*%<
+ * Reopens files established by dns_dt_create().
+ *
+ * If 'roll' is non-negative and 'env->mode' is dns_dtmode_file,
+ * then the file is automatically rolled over before reopening.
+ * The value of 'roll' indicates the number of backup log files to
+ * keep. If 'roll' is negative, or if 'env->mode' is dns_dtmode_unix,
+ * then the channel is simply reopened.
+ *
+ * Note: dns_dt_reopen() must be called in task exclusive mode.
+ *
+ * Requires:
+ *\li 'env' is a valid dnstap environment.
+ */
+
+isc_result_t
+dns_dt_setidentity(dns_dtenv_t *env, const char *identity);
+isc_result_t
+dns_dt_setversion(dns_dtenv_t *env, const char *version);
+/*%<
+ * Set the "identity" and "version" strings to be sent in dnstap messages.
+ *
+ * Requires:
+ *
+ *\li 'env' is a valid dnstap environment.
+ */
+
+void
+dns_dt_attach(dns_dtenv_t *source, dns_dtenv_t **destp);
+/*%<
+ * Attach '*destp' to 'source', incrementing the reference counter.
+ *
+ * Requires:
+ *
+ *\li 'source' is a valid dnstap environment.
+ *
+ *\li 'destp' is not NULL and '*destp' is NULL.
+ *
+ *\li *destp is attached to source.
+ */
+
+void
+dns_dt_detach(dns_dtenv_t **envp);
+/*%<
+ * Detach '*envp', decrementing the reference counter.
+ *
+ * Requires:
+ *
+ *\li '*envp' is a valid dnstap environment.
+ *
+ * Ensures:
+ *
+ *\li '*envp' will be destroyed when the number of references reaches zero.
+ *
+ *\li '*envp' is NULL.
+ */
+
+isc_result_t
+dns_dt_getstats(dns_dtenv_t *env, isc_stats_t **statsp);
+/*%<
+ * Attach to the stats struct if it exists.
+ *
+ * Requires:
+ *
+ *\li 'env' is a valid dnstap environment.
+ *
+ *\li 'statsp' is non NULL and '*statsp' is NULL.
+ *
+ * Returns:
+ *
+ *\li ISC_R_SUCCESS
+ *
+ *\li ISC_R_NOTFOUND
+ */
+
+void
+dns_dt_shutdown(void);
+/*%<
+ * Shuts down dnstap and frees global resources. This function must only
+ * be called immediately before server shutdown.
+ */
+
+void
+dns_dt_send(dns_view_t *view, dns_dtmsgtype_t msgtype,
+ isc_sockaddr_t *qaddr, isc_sockaddr_t *dstaddr,
+ bool tcp, isc_region_t *zone, isc_time_t *qtime,
+ isc_time_t *rtime, isc_buffer_t *buf);
+/*%<
+ * Sends a dnstap message to the log, if 'msgtype' is one of the message
+ * types represented in 'view->dttypes'.
+ *
+ * Parameters are: 'qaddr' (query address, i.e, the address of the
+ * query initiator); 'raddr' (response address, i.e., the address of
+ * the query responder); 'tcp' (boolean indicating whether the transaction
+ * was over TCP); 'zone' (the authoritative zone or bailiwick, in
+ * uncompressed wire format), 'qtime' and 'rtime' (query and response
+ * times; if NULL, they are set to the current time); and 'buf' (the
+ * DNS message being logged, in wire format).
+ *
+ * Requires:
+ *
+ *\li 'view' is a valid view, and 'view->dtenv' is NULL or is a
+ * valid dnstap environment.
+ */
+
+isc_result_t
+dns_dt_parse(isc_mem_t *mctx, isc_region_t *src, dns_dtdata_t **destp);
+/*%<
+ * Converts a raw dnstap frame in 'src' to a parsed dnstap data structure
+ * in '*destp'.
+ *
+ * Requires:
+ *\li 'src' is not NULL
+ *
+ *\li 'destp' is not NULL and '*destp' points to a valid buffer.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS on success
+ *
+ *\li Other errors are possible.
+ */
+
+isc_result_t
+dns_dt_datatotext(dns_dtdata_t *d, isc_buffer_t **dest);
+/*%<
+ * Converts a parsed dnstap data structure 'd' to text, storing
+ * the result in the buffer 'dest'. If 'dest' points to a dynamically
+ * allocated buffer, then it may be reallocated as needed.
+ *
+ * (XXX: add a 'long_form' option to generate a detailed listing of
+ * dnstap data instead * of a one-line summary.)
+ *
+ * Requires:
+ *\li 'd' is not NULL
+ *
+ *\li 'dest' is not NULL and '*dest' points to a valid buffer.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS on success
+ *\li #ISC_R_NOSPACE if buffer is not dynamic and runs out of space
+ *\li #ISC_R_NOMEMORY if buffer is dynamic but memory could not be allocated
+ *
+ *\li Other errors are possible.
+ */
+
+void
+dns_dtdata_free(dns_dtdata_t **dp);
+/*%<
+ * Frees the specified dns_dtdata structure and all its members,
+ * and sets *dp to NULL.
+ */
+
+isc_result_t
+dns_dt_open(const char *filename, dns_dtmode_t mode,
+ isc_mem_t *mctx, dns_dthandle_t **handlep);
+/*%<
+ * Opens a dnstap framestream at 'filename' and stores a pointer to the
+ * reader object in a dns_dthandle_t structure.
+ *
+ * The caller is responsible for allocating the handle structure.
+ *
+ * (XXX: Currently only file readers are supported, not unix-domain socket
+ * readers.)
+ *
+ * Requires:
+ *
+ *\li 'filename' is not NULL.
+ *
+ *\li 'handlep' is not NULL and '*handlep' is NULL.
+ *
+ *\li '*mctx' is not a valid memory context.
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS on success
+ *\li #ISC_R_NOTIMPLEMENTED if 'mode' is not dns_dtmode_file. (XXX)
+ *\li #ISC_R_NOMEMORY if the fstrm library was unable to allocate a
+ * reader or options structure
+ *\li #ISC_R_FAILURE if 'filename' could not be opened.
+ *\li #DNS_R_BADDNSTAP if 'filename' does not contain a dnstap
+ * framestream.
+ */
+
+isc_result_t
+dns_dt_getframe(dns_dthandle_t *handle, uint8_t **bufp, size_t *sizep);
+/*%<
+ * Read a dnstap frame from the framstream reader in 'handle', storing
+ * a pointer to it in '*bufp' and its size in '*sizep'.
+ *
+ * Requires:
+ *
+ *\li 'handle' is not NULL
+ *\li 'bufp' is not NULL
+ *\li 'sizep' is not NULL
+ *
+ * Ensures:
+ * \li if returning ISC_R_SUCCESS then '*bufp' is not NULL
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS on success
+ *\li #ISC_R_NOMORE at the end of the frame stream
+ *\li #ISC_R_FAILURE for any other failure
+ */
+
+void
+dns_dt_close(dns_dthandle_t **handlep);
+/*%<
+ * Closes the dnstap file referenced by 'handle'.
+ *
+ * Requires:
+ *
+ *\li '*handlep' is not NULL
+ */
+
+#endif /* _DNSTAP_H */
diff --git a/lib/dns/include/dns/ds.h b/lib/dns/include/dns/ds.h
new file mode 100644
index 0000000..4ea5a0d
--- /dev/null
+++ b/lib/dns/include/dns/ds.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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)
+#define DNS_DSDIGEST_GOST (3)
+#define DNS_DSDIGEST_SHA384 (4)
+
+/*
+ * Assuming SHA-384 digest type.
+ */
+#define DNS_DS_BUFFERSIZE (52)
+
+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_LANG_ENDDECLS
+
+#endif /* DNS_DS_H */
diff --git a/lib/dns/include/dns/dsdigest.h b/lib/dns/include/dns/dsdigest.h
new file mode 100644
index 0000000..b029ecd
--- /dev/null
+++ b/lib/dns/include/dns/dsdigest.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#ifndef DNS_DSDIGEST_H
+#define DNS_DSDIGEST_H 1
+
+/*! \file dns/dsdigest.h */
+
+#include <isc/lang.h>
+
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+dns_dsdigest_fromtext(dns_dsdigest_t *dsdigestp, isc_textregion_t *source);
+/*%<
+ * Convert the text 'source' refers to into a DS/DLV digest type value.
+ * The text may contain either a mnemonic digest name or a decimal
+ * digest number.
+ *
+ * Requires:
+ *\li 'dsdigestp' 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_dsdigest_totext(dns_dsdigest_t dsdigest, isc_buffer_t *target);
+/*%<
+ * Put a textual representation of the DS/DLV digest type 'dsdigest'
+ * into 'target'.
+ *
+ * Requires:
+ *\li 'dsdigest' is a valid dsdigest.
+ *
+ *\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
+ */
+
+#define DNS_DSDIGEST_FORMATSIZE 20
+void
+dns_dsdigest_format(dns_dsdigest_t typ, char *cp, unsigned int size);
+/*%<
+ * Wrapper for dns_dsdigest_totext(), writing text into 'cp'
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_DSDIGEST_H */
diff --git a/lib/dns/include/dns/dyndb.h b/lib/dns/include/dns/dyndb.h
new file mode 100644
index 0000000..d5fedb5
--- /dev/null
+++ b/lib/dns/include/dns/dyndb.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef DNS_DYNDB_H
+#define DNS_DYNDB_H
+
+#include <stdbool.h>
+
+#include <isc/types.h>
+
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+/*!
+ * \brief
+ * Context for intializing a dyndb module.
+ *
+ * This structure passes global server data to which a dyndb
+ * module will need access -- the server memory context, hash
+ * initializer, log context, etc. The structure doesn't persist
+ * beyond configuring the dyndb module. The module's register function
+ * should attach to all reference-counted variables and its destroy
+ * function should detach from them.
+ */
+struct dns_dyndbctx {
+ unsigned int magic;
+ const void *hashinit;
+ isc_mem_t *mctx;
+ isc_log_t *lctx;
+ dns_view_t *view;
+ dns_zonemgr_t *zmgr;
+ isc_task_t *task;
+ isc_timermgr_t *timermgr;
+ bool *refvar;
+};
+
+#define DNS_DYNDBCTX_MAGIC ISC_MAGIC('D', 'd', 'b', 'c')
+#define DNS_DYNDBCTX_VALID(d) ISC_MAGIC_VALID(d, DNS_DYNDBCTX_MAGIC)
+
+/*
+ * API version
+ *
+ * When the API changes, increment DNS_DYNDB_VERSION. If the
+ * change is backward-compatible (e.g., adding a new function call
+ * but not changing or removing an old one), increment DNS_DYNDB_AGE;
+ * if not, set DNS_DYNDB_AGE to 0.
+ */
+#ifndef DNS_DYNDB_VERSION
+#define DNS_DYNDB_VERSION 1
+#define DNS_DYNDB_AGE 0
+#endif
+
+typedef isc_result_t dns_dyndb_register_t(isc_mem_t *mctx,
+ const char *name,
+ const char *parameters,
+ const char *file,
+ unsigned long line,
+ const dns_dyndbctx_t *dctx,
+ void **instp);
+/*%
+ * Called when registering a new driver instance. 'name' must be unique.
+ * 'parameters' contains the driver configuration text. 'dctx' is the
+ * initialization context set up in dns_dyndb_createctx().
+ *
+ * '*instp' must be set to the driver instance handle if the functino
+ * is successful.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ *\li Other errors are possible
+ */
+
+typedef void dns_dyndb_destroy_t(void **instp);
+/*%
+ * Destroy a driver instance. Dereference any reference-counted
+ * variables passed in 'dctx' and 'inst' in the register function.
+ *
+ * \c *instp must be set to \c NULL by the function before it returns.
+ */
+
+typedef int dns_dyndb_version_t(unsigned int *flags);
+/*%
+ * Return the API version number a dyndb module was compiled with.
+ *
+ * If the returned version number is no greater than than
+ * DNS_DYNDB_VERSION, and no less than DNS_DYNDB_VERSION - DNS_DYNDB_AGE,
+ * then the module is API-compatible with named.
+ *
+ * 'flags' is currently unused and may be NULL, but could be used in
+ * the future to pass back driver capabilities or other information.
+ */
+
+isc_result_t
+dns_dyndb_load(const char *libname, const char *name, const char *parameters,
+ const char *file, unsigned long line, isc_mem_t *mctx,
+ const dns_dyndbctx_t *dctx);
+/*%
+ * Load a dyndb module.
+ *
+ * This loads a dyndb module using dlopen() or equivalent, calls its register
+ * function (see dns_dyndb_register_t above), and if successful, adds
+ * the instance handle to a list of dyndb instances so it can be cleaned
+ * up later.
+ *
+ * 'file' and 'line' can be used to indicate the name of the file and
+ * the line number from which the parameters were taken, so that logged
+ * error messages, if any, will display the correct locations.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ *\li Other errors are possible
+ */
+
+void
+dns_dyndb_cleanup(bool exiting);
+/*%
+ * Shut down and destroy all running dyndb modules.
+ *
+ * 'exiting' indicates whether the server is shutting down,
+ * as opposed to merely being reconfigured.
+ */
+
+isc_result_t
+dns_dyndb_createctx(isc_mem_t *mctx, const void *hashinit, isc_log_t *lctx,
+ dns_view_t *view, dns_zonemgr_t *zmgr, isc_task_t *task,
+ isc_timermgr_t *tmgr, dns_dyndbctx_t **dctxp);
+/*%
+ * Create a dyndb initialization context structure, with
+ * pointers to structures in the server that the dyndb module will
+ * need to access (view, zone manager, memory context, hash initializer,
+ * etc). This structure is expected to last only until all dyndb
+ * modules have been loaded and initialized; after that it will be
+ * destroyed with dns_dyndb_destroyctx().
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ *\li Other errors are possible
+ */
+
+void
+dns_dyndb_destroyctx(dns_dyndbctx_t **dctxp);
+/*%
+ * Destroys a dyndb initialization context structure; all
+ * reference-counted members are detached and the structure is freed.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_DYNDB_H */
diff --git a/lib/dns/include/dns/ecdb.h b/lib/dns/include/dns/ecdb.h
new file mode 100644
index 0000000..6d6c880
--- /dev/null
+++ b/lib/dns/include/dns/ecdb.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#ifndef DNS_ECDB_H
+#define DNS_ECDB_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/* TBD */
+
+/***
+ *** Imports
+ ***/
+
+#include <dns/types.h>
+
+/***
+ *** Types
+ ***/
+
+/***
+ *** Functions
+ ***/
+
+ISC_LANG_BEGINDECLS
+
+/* TBD: describe those */
+
+isc_result_t
+dns_ecdb_register(isc_mem_t *mctx, dns_dbimplementation_t **dbimp);
+
+void
+dns_ecdb_unregister(dns_dbimplementation_t **dbimp);
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_ECDB_H */
diff --git a/lib/dns/include/dns/edns.h b/lib/dns/include/dns/edns.h
new file mode 100644
index 0000000..018c89d
--- /dev/null
+++ b/lib/dns/include/dns/edns.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef DNS_EDNS_H
+#define DNS_EDNS_H 1
+
+/*%
+ * The maximum version on EDNS supported by this build.
+ */
+#define DNS_EDNS_VERSION 0
+#ifdef DRAFT_ANDREWS_EDNS1
+#undef DNS_EDNS_VERSION
+/*
+ * Warning: this currently disables sending COOKIE requests in resolver.c
+ */
+#define DNS_EDNS_VERSION 1 /* draft-andrews-edns1 */
+#endif
+
+#endif
diff --git a/lib/dns/include/dns/events.h b/lib/dns/include/dns/events.h
new file mode 100644
index 0000000..bd051c1
--- /dev/null
+++ b/lib/dns/include/dns/events.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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_RBTDEADNODES (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_RBTPRUNE (ISC_EVENTCLASS_DNS + 41)
+#define DNS_EVENT_MANAGEKEYS (ISC_EVENTCLASS_DNS + 42)
+#define DNS_EVENT_CLIENTRESDONE (ISC_EVENTCLASS_DNS + 43)
+#define DNS_EVENT_CLIENTREQDONE (ISC_EVENTCLASS_DNS + 44)
+#define DNS_EVENT_ADBGROWENTRIES (ISC_EVENTCLASS_DNS + 45)
+#define DNS_EVENT_ADBGROWNAMES (ISC_EVENTCLASS_DNS + 46)
+#define DNS_EVENT_ZONESECURESERIAL (ISC_EVENTCLASS_DNS + 47)
+#define DNS_EVENT_ZONESECUREDB (ISC_EVENTCLASS_DNS + 48)
+#define DNS_EVENT_ZONELOAD (ISC_EVENTCLASS_DNS + 49)
+#define DNS_EVENT_KEYDONE (ISC_EVENTCLASS_DNS + 50)
+#define DNS_EVENT_SETNSEC3PARAM (ISC_EVENTCLASS_DNS + 51)
+#define DNS_EVENT_SETSERIAL (ISC_EVENTCLASS_DNS + 52)
+#define DNS_EVENT_CATZUPDATED (ISC_EVENTCLASS_DNS + 53)
+#define DNS_EVENT_CATZADDZONE (ISC_EVENTCLASS_DNS + 54)
+#define DNS_EVENT_CATZMODZONE (ISC_EVENTCLASS_DNS + 55)
+#define DNS_EVENT_CATZDELZONE (ISC_EVENTCLASS_DNS + 56)
+#define DNS_EVENT_STARTUPDATE (ISC_EVENTCLASS_DNS + 58)
+
+#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..6a9b555
--- /dev/null
+++ b/lib/dns/include/dns/fixedname.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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. This is typically used for stack-allocated names.
+ *
+ * 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 <isc/lang.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];
+};
+
+ISC_LANG_BEGINDECLS
+
+void
+dns_fixedname_init(dns_fixedname_t *fixed);
+
+void
+dns_fixedname_invalidate(dns_fixedname_t *fixed);
+
+dns_name_t *
+dns_fixedname_name(dns_fixedname_t *fixed);
+
+dns_name_t *
+dns_fixedname_initname(dns_fixedname_t *fixed);
+
+ISC_LANG_ENDDECLS
+
+#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..eb0e045
--- /dev/null
+++ b/lib/dns/include/dns/forward.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef DNS_FORWARD_H
+#define DNS_FORWARD_H 1
+
+/*! \file dns/forward.h */
+
+#include <isc/lang.h>
+#include <isc/result.h>
+#include <isc/sockaddr.h>
+
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+struct dns_forwarder {
+ isc_sockaddr_t addr;
+ isc_dscp_t dscp;
+ ISC_LINK(dns_forwarder_t) link;
+};
+
+typedef ISC_LIST(struct dns_forwarder) dns_forwarderlist_t;
+
+struct dns_forwarders {
+ dns_forwarderlist_t fwdrs;
+ 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_addfwd(dns_fwdtable_t *fwdtable, dns_name_t *name,
+ dns_forwarderlist_t *fwdrs, dns_fwdpolicy_t policy);
+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/fwdrs 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/fwdrs is a valid list of isc_sockaddr/dns_forwarder
+ * structures, which may be empty.
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS
+ * \li #ISC_R_NOMEMORY
+ */
+
+isc_result_t
+dns_fwdtable_delete(dns_fwdtable_t *fwdtable, dns_name_t *name);
+/*%<
+ * Removes an entry for 'name' from the forwarding table. If an entry
+ * that exactly matches 'name' does not exist, ISC_R_NOTFOUND will be returned.
+ *
+ * Requires:
+ * \li fwdtable is a valid forwarding table.
+ * \li name is a valid name
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS
+ * \li #ISC_R_NOTFOUND
+ */
+
+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/geoip.h b/lib/dns/include/dns/geoip.h
new file mode 100644
index 0000000..d9028e5
--- /dev/null
+++ b/lib/dns/include/dns/geoip.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef DNS_GEOIP_H
+#define DNS_GEOIP_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file dns/acl.h
+ * \brief
+ * Address match list handling.
+ */
+
+/***
+ *** Imports
+ ***/
+
+#include <stdbool.h>
+
+#include <isc/lang.h>
+#include <isc/magic.h>
+#include <isc/netaddr.h>
+
+#include <dns/name.h>
+#include <dns/types.h>
+#include <dns/iptable.h>
+
+#ifdef HAVE_GEOIP
+#include <GeoIP.h>
+#else
+typedef void GeoIP;
+#endif
+
+/***
+ *** Types
+ ***/
+
+typedef enum {
+ dns_geoip_countrycode,
+ dns_geoip_countrycode3,
+ dns_geoip_countryname,
+ dns_geoip_region,
+ dns_geoip_regionname,
+ dns_geoip_country_code,
+ dns_geoip_country_code3,
+ dns_geoip_country_name,
+ dns_geoip_region_countrycode,
+ dns_geoip_region_code,
+ dns_geoip_region_name,
+ dns_geoip_city_countrycode,
+ dns_geoip_city_countrycode3,
+ dns_geoip_city_countryname,
+ dns_geoip_city_region,
+ dns_geoip_city_regionname,
+ dns_geoip_city_name,
+ dns_geoip_city_postalcode,
+ dns_geoip_city_metrocode,
+ dns_geoip_city_areacode,
+ dns_geoip_city_continentcode,
+ dns_geoip_city_timezonecode,
+ dns_geoip_isp_name,
+ dns_geoip_org_name,
+ dns_geoip_as_asnum,
+ dns_geoip_domain_name,
+ dns_geoip_netspeed_id
+} dns_geoip_subtype_t;
+
+typedef struct dns_geoip_elem {
+ dns_geoip_subtype_t subtype;
+ GeoIP *db;
+ union {
+ char as_string[256];
+ int as_int;
+ };
+} dns_geoip_elem_t;
+
+typedef struct dns_geoip_databases {
+ GeoIP *country_v4; /* DB 1 */
+ GeoIP *city_v4; /* DB 2 or 6 */
+ GeoIP *region; /* DB 3 or 7 */
+ GeoIP *isp; /* DB 4 */
+ GeoIP *org; /* DB 5 */
+ GeoIP *as; /* DB 9 */
+ GeoIP *netspeed; /* DB 10 */
+ GeoIP *domain; /* DB 11 */
+ GeoIP *country_v6; /* DB 12 */
+ GeoIP *city_v6; /* DB 30 or 31 */
+} dns_geoip_databases_t;
+
+/***
+ *** Functions
+ ***/
+
+ISC_LANG_BEGINDECLS
+
+bool
+dns_geoip_match(const isc_netaddr_t *reqaddr, uint8_t *scope,
+ const dns_geoip_databases_t *geoip,
+ const dns_geoip_elem_t *elt);
+
+void
+dns_geoip_shutdown(void);
+
+ISC_LANG_ENDDECLS
+#endif /* DNS_GEOIP_H */
diff --git a/lib/dns/include/dns/ipkeylist.h b/lib/dns/include/dns/ipkeylist.h
new file mode 100644
index 0000000..5b012b4
--- /dev/null
+++ b/lib/dns/include/dns/ipkeylist.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef DNS_IPKEYLIST_H
+#define DNS_IPKEYLIST_H 1
+
+#include <inttypes.h>
+
+#include <isc/types.h>
+#include <dns/types.h>
+
+/*%
+ * A structure holding a list of addresses, dscps and keys. Used to
+ * store masters for a slave zone, created by parsing config options.
+ */
+struct dns_ipkeylist {
+ isc_sockaddr_t *addrs;
+ isc_dscp_t *dscps;
+ dns_name_t **keys;
+ dns_name_t **labels;
+ uint32_t count;
+ uint32_t allocated;
+};
+
+void
+dns_ipkeylist_init(dns_ipkeylist_t *ipkl);
+/*%<
+ * Reset ipkl to empty state
+ *
+ * Requires:
+ *\li 'ipkl' to be non NULL.
+ */
+
+void
+dns_ipkeylist_clear(isc_mem_t *mctx, dns_ipkeylist_t *ipkl);
+/*%<
+ * Free `ipkl` contents using `mctx`.
+ *
+ * After this call, `ipkl` is a freshly cleared structure with all
+ * pointers set to `NULL` and count set to 0.
+ *
+ * Requires:
+ *\li 'mctx' to be a valid memory context.
+ *\li 'ipkl' to be non NULL.
+ */
+
+isc_result_t
+dns_ipkeylist_copy(isc_mem_t *mctx, const dns_ipkeylist_t *src,
+ dns_ipkeylist_t *dst);
+/*%<
+ * Deep copy `src` into empty `dst`, allocating `dst`'s contents.
+ *
+ * Requires:
+ *\li 'mctx' to be a valid memory context.
+ *\li 'src' to be non NULL
+ *\li 'dst' to be non NULL and point to an empty \ref dns_ipkeylist_t
+ * with all pointers set to `NULL` and count set to 0.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS -- success
+ *\li any other value -- failure
+ */
+isc_result_t
+dns_ipkeylist_resize(isc_mem_t *mctx, dns_ipkeylist_t *ipkl, unsigned int n);
+/*%<
+ * Resize ipkl to contain n elements. Size (count) is not changed, and the
+ * added space is zeroed.
+ *
+ * Requires:
+ * \li 'mctx' to be a valid memory context.
+ * \li 'ipk' to be non NULL
+ * \li 'n' >= ipkl->count
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS if successs
+ * \li #ISC_R_NOMEMORY if there's no memory, ipkeylist is left untoched
+ */
+
+#endif
diff --git a/lib/dns/include/dns/iptable.h b/lib/dns/include/dns/iptable.h
new file mode 100644
index 0000000..26a6ad5
--- /dev/null
+++ b/lib/dns/include/dns/iptable.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#ifndef DNS_IPTABLE_H
+#define DNS_IPTABLE_H 1
+
+#include <stdbool.h>
+
+#include <isc/lang.h>
+#include <isc/magic.h>
+#include <isc/radix.h>
+
+#include <dns/types.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,
+ uint16_t bitlen, bool pos);
+isc_result_t
+dns_iptable_addprefix2(dns_iptable_t *tab, isc_netaddr_t *addr,
+ uint16_t bitlen, bool pos,
+ bool is_ecs);
+/*
+ * Add an IP prefix to an existing IP table
+ */
+
+isc_result_t
+dns_iptable_merge(dns_iptable_t *tab, dns_iptable_t *source, bool 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..8bcb551
--- /dev/null
+++ b/lib/dns/include/dns/journal.h
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#ifndef DNS_JOURNAL_H
+#define DNS_JOURNAL_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file dns/journal.h
+ * \brief
+ * Database journaling.
+ */
+
+/***
+ *** Imports
+ ***/
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#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
+
+#define DNS_JOURNAL_READ 0x00000000 /* false */
+#define DNS_JOURNAL_CREATE 0x00000001 /* true */
+#define DNS_JOURNAL_WRITE 0x00000002
+
+/***
+ *** 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, unsigned int mode,
+ dns_journal_t **journalp);
+/*%<
+ * Open the journal file 'filename' and create a dns_journal_t object for it.
+ *
+ * DNS_JOURNAL_CREATE open the journal for reading and writing and create
+ * the journal if it does not exist.
+ * DNS_JOURNAL_WRITE open the journal for reading and writing.
+ * DNS_JOURNAL_READ open the journal for reading only.
+ */
+
+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.
+ */
+
+uint32_t
+dns_journal_first_serial(dns_journal_t *j);
+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,
+ uint32_t begin_serial, 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 addressable
+ * 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, 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);
+
+isc_result_t
+dns_db_diffx(dns_diff_t *diff, 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 diff/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' if
+ * non-NULL.
+ */
+
+isc_result_t
+dns_journal_compact(isc_mem_t *mctx, char *filename, uint32_t serial,
+ 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.
+ */
+
+bool
+dns_journal_get_sourceserial(dns_journal_t *j, uint32_t *sourceserial);
+void
+dns_journal_set_sourceserial(dns_journal_t *j, uint32_t sourceserial);
+/*%<
+ * Get and set source serial.
+ *
+ * Returns:
+ * true if sourceserial has previously been set.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_JOURNAL_H */
diff --git a/lib/dns/include/dns/keydata.h b/lib/dns/include/dns/keydata.h
new file mode 100644
index 0000000..f12932a
--- /dev/null
+++ b/lib/dns/include/dns/keydata.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#ifndef DNS_KEYDATA_H
+#define DNS_KEYDATA_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file dns/keydata.h
+ * \brief
+ * KEYDATA utilities.
+ */
+
+/***
+ *** Imports
+ ***/
+
+#include <inttypes.h>
+
+#include <isc/lang.h>
+#include <isc/types.h>
+
+#include <dns/types.h>
+#include <dns/rdatastruct.h>
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+dns_keydata_todnskey(dns_rdata_keydata_t *keydata,
+ dns_rdata_dnskey_t *dnskey, isc_mem_t *mctx);
+
+isc_result_t
+dns_keydata_fromdnskey(dns_rdata_keydata_t *keydata,
+ dns_rdata_dnskey_t *dnskey,
+ uint32_t refresh, uint32_t addhd,
+ uint32_t removehd, isc_mem_t *mctx);
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_KEYDATA_H */
diff --git a/lib/dns/include/dns/keyflags.h b/lib/dns/include/dns/keyflags.h
new file mode 100644
index 0000000..6d41a1c
--- /dev/null
+++ b/lib/dns/include/dns/keyflags.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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..a94fa3e
--- /dev/null
+++ b/lib/dns/include/dns/keytable.h
@@ -0,0 +1,439 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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 <stdbool.h>
+
+#include <isc/lang.h>
+#include <isc/magic.h>
+#include <isc/rwlock.h>
+#include <isc/stdtime.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, bool managed,
+ dst_key_t **keyp);
+/*%<
+ * Add '*keyp' to 'keytable' (using the name in '*keyp').
+ * The value of keynode->managed is set to 'managed'
+ *
+ * Notes:
+ *
+ *\li Ownership of *keyp is transferred to the keytable.
+ *\li If the key already exists in the table, ISC_R_EXISTS is
+ * returned and the new key is freed.
+ *
+ * Requires:
+ *
+ *\li 'keytable' points to a valid keytable.
+ *
+ *\li keyp != NULL && *keyp is a valid dst_key_t *.
+ *
+ * Ensures:
+ *
+ *\li On success, *keyp == NULL
+ *
+ * Returns:
+ *
+ *\li ISC_R_SUCCESS
+ *\li ISC_R_EXISTS
+ *
+ *\li Any other result indicates failure.
+ */
+
+isc_result_t
+dns_keytable_marksecure(dns_keytable_t *keytable, dns_name_t *name);
+/*%<
+ * Add a null key to 'keytable' for name 'name'. This marks the
+ * name as a secure domain, but doesn't supply any key data to allow the
+ * domain to be validated. (Used when automated trust anchor management
+ * has gotten broken by a zone misconfiguration; for example, when the
+ * active key has been revoked but the stand-by key was still in its 30-day
+ * waiting period for validity.)
+ *
+ * Notes:
+ *
+ *\li If a key already exists in the table, ISC_R_EXISTS is
+ * returned and nothing is done.
+ *
+ * Requires:
+ *
+ *\li 'keytable' points to a valid keytable.
+ *
+ *\li keyp != NULL && *keyp is a valid dst_key_t *.
+ *
+ * Returns:
+ *
+ *\li ISC_R_SUCCESS
+ *\li ISC_R_EXISTS
+ *
+ *\li Any other result indicates failure.
+ */
+
+isc_result_t
+dns_keytable_delete(dns_keytable_t *keytable, dns_name_t *keyname);
+/*%<
+ * Delete node(s) from 'keytable' matching name 'keyname'
+ *
+ * Requires:
+ *
+ *\li 'keytable' points to a valid keytable.
+ *
+ *\li 'name' is not NULL
+ *
+ * Returns:
+ *
+ *\li ISC_R_SUCCESS
+ *
+ *\li Any other result indicates failure.
+ */
+
+isc_result_t
+dns_keytable_deletekeynode(dns_keytable_t *keytable, dst_key_t *dstkey);
+/*%<
+ * Delete node(s) from 'keytable' containing copies of the key pointed
+ * to by 'dstkey'
+ *
+ * Requires:
+ *
+ *\li 'keytable' points to a valid keytable.
+ *\li 'dstkey' is not NULL
+ *
+ * Returns:
+ *
+ *\li ISC_R_SUCCESS
+ *
+ *\li Any other result indicates failure.
+ */
+
+isc_result_t
+dns_keytable_find(dns_keytable_t *keytable, dns_name_t *keyname,
+ dns_keynode_t **keynodep);
+/*%<
+ * Search for the first instance of a key named 'name' in 'keytable',
+ * without regard to keyid and algorithm. Use dns_keytable_nextkeynode()
+ * to find subsequent 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 ISC_R_NOTFOUND
+ *
+ *\li Any other result indicates an error.
+ */
+
+isc_result_t
+dns_keytable_nextkeynode(dns_keytable_t *keytable, dns_keynode_t *keynode,
+ dns_keynode_t **nextnodep);
+/*%<
+ * Return for the next key after 'keynode' in 'keytable', without regard to
+ * keyid and algorithm.
+ *
+ * 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_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_attachkeynode(dns_keytable_t *keytable, dns_keynode_t *source,
+ dns_keynode_t **target);
+/*%<
+ * Attach a keynode and and increment the active_nodes counter in a
+ * corresponding keytable.
+ *
+ * Requires:
+ *
+ *\li 'keytable' is a valid keytable.
+ *
+ *\li 'source' is a valid keynode.
+ *
+ *\li 'target' is not null and '*target' is null.
+ */
+
+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,
+ dns_name_t *foundname, bool *wantdnssecp);
+/*%<
+ * Is 'name' at or beneath a trusted key?
+ *
+ * Requires:
+ *
+ *\li 'keytable' is a valid keytable.
+ *
+ *\li 'name' is a valid absolute name.
+ *
+ *\li 'foundanme' is NULL or is a pointer to an initialized dns_name_t
+ *
+ *\li '*wantsdnssecp' is a valid bool.
+
+ * Ensures:
+ *
+ *\li On success, *wantsdnssecp will be true if and only if 'name'
+ * is at or beneath a trusted key. If 'foundname' is not NULL, then
+ * it will be updated to contain the name of the closest enclosing
+ * trust anchor.
+ *
+ * Returns:
+ *
+ *\li ISC_R_SUCCESS
+ *
+ *\li Any other result is an error.
+ */
+
+isc_result_t
+dns_keytable_dump(dns_keytable_t *keytable, FILE *fp);
+/*%<
+ * Dump the keytable on fp.
+ */
+
+isc_result_t
+dns_keytable_totext(dns_keytable_t *keytable, isc_buffer_t **buf);
+/*%<
+ * Dump the keytable to buffer at 'buf'
+ */
+
+dst_key_t *
+dns_keynode_key(dns_keynode_t *keynode);
+/*%<
+ * Get the DST key associated with keynode.
+ */
+
+bool
+dns_keynode_managed(dns_keynode_t *keynode);
+/*%<
+ * Is this flagged as a managed key?
+ */
+
+isc_result_t
+dns_keynode_create(isc_mem_t *mctx, dns_keynode_t **target);
+/*%<
+ * Allocate space for a keynode
+ */
+
+void
+dns_keynode_attach(dns_keynode_t *source, dns_keynode_t **target);
+/*%<
+ * Attach keynode 'source' to '*target'
+ */
+
+void
+dns_keynode_detach(isc_mem_t *mctx, dns_keynode_t **target);
+/*%<
+ * Detach a single keynode, without touching any keynodes that
+ * may be pointed to by its 'next' pointer
+ */
+
+void
+dns_keynode_detachall(isc_mem_t *mctx, dns_keynode_t **target);
+/*%<
+ * Detach a keynode and all its succesors.
+ */
+
+isc_result_t
+dns_keytable_forall(dns_keytable_t *keytable,
+ void (*func)(dns_keytable_t *, dns_keynode_t *, void *),
+ void *arg);
+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..ad17261
--- /dev/null
+++ b/lib/dns/include/dns/keyvalues.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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_REVOKE 0x0080 /*%< key revoked (per rfc5011) */
+#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_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_RSASHA256 8
+#define DNS_KEYALG_RSASHA512 10
+#define DNS_KEYALG_ECCGOST 12
+#define DNS_KEYALG_ECDSA256 13
+#define DNS_KEYALG_ECDSA384 14
+#define DNS_KEYALG_ED25519 15
+#define DNS_KEYALG_ED448 16
+#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
+
+#define DNS_SIG_GOSTSIGSIZE 64
+
+#define DNS_SIG_ECDSA256SIZE 64
+#define DNS_SIG_ECDSA384SIZE 96
+
+#define DNS_KEY_ECDSA256SIZE 64
+#define DNS_KEY_ECDSA384SIZE 96
+
+#define DNS_SIG_ED25519SIZE 64
+#define DNS_SIG_ED448SIZE 114
+
+#define DNS_KEY_ED25519SIZE 32
+#define DNS_KEY_ED448SIZE 57
+
+#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..d63efdd
--- /dev/null
+++ b/lib/dns/include/dns/lib.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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_result_t
+dns_lib_init(void);
+/*%<
+ * A set of initialization procedure used in the DNS library. This function
+ * is provided for an application that is not aware of the underlying ISC or
+ * DNS libraries much.
+ */
+
+void
+dns_lib_shutdown(void);
+/*%<
+ * Free temporary resources allocated in dns_lib_init().
+ */
+
+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..278ada0
--- /dev/null
+++ b/lib/dns/include/dns/log.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file dns/log.h
+ */
+
+#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])
+#define DNS_LOGCATEGORY_RPZ (&dns_categories[12])
+#define DNS_LOGCATEGORY_RRL (&dns_categories[13])
+#define DNS_LOGCATEGORY_CNAME (&dns_categories[14])
+#define DNS_LOGCATEGORY_SPILL (&dns_categories[15])
+#define DNS_LOGCATEGORY_DNSTAP (&dns_categories[16])
+
+/* 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])
+#define DNS_LOGMODULE_DNSSEC (&dns_modules[27])
+#define DNS_LOGMODULE_CRYPTO (&dns_modules[28])
+#define DNS_LOGMODULE_PACKETS (&dns_modules[29])
+#define DNS_LOGMODULE_NTA (&dns_modules[30])
+#define DNS_LOGMODULE_DYNDB (&dns_modules[31])
+#define DNS_LOGMODULE_DNSTAP (&dns_modules[32])
+#define DNS_LOGMODULE_SSU (&dns_modules[33])
+
+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 categories 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..1af58fe
--- /dev/null
+++ b/lib/dns/include/dns/lookup.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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
+ * resolving 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..61c93b0
--- /dev/null
+++ b/lib/dns/include/dns/master.h
@@ -0,0 +1,382 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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
+#define DNS_MASTER_KEY 0x00004000 /*%< Loading a key zone master file. */
+#define DNS_MASTER_NOTTL 0x00008000 /*%< Don't require ttl. */
+#define DNS_MASTER_CHECKTTL 0x00010000 /*%< Check max-zone-ttl */
+
+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 1
+
+/*
+ * Flags to indicate the status of the data in the raw file header
+ */
+#define DNS_MASTERRAW_COMPAT 0x01
+#define DNS_MASTERRAW_SOURCESERIALSET 0x02
+#define DNS_MASTERRAW_LASTXFRINSET 0x04
+
+/* Common header */
+struct dns_masterrawheader {
+ uint32_t format; /* must be
+ * dns_masterformat_raw
+ * or
+ * dns_masterformat_map */
+ uint32_t version; /* compatibility for future
+ * extensions */
+ uint32_t dumptime; /* timestamp on creation
+ * (currently unused) */
+ uint32_t flags; /* Flags */
+ uint32_t sourceserial; /* Source serial number (used
+ * by inline-signing zones) */
+ uint32_t lastxfrin; /* timestamp of last transfer
+ * (used by slave zones) */
+};
+
+/* The structure for each RRset */
+typedef struct {
+ 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 */
+ uint32_t nrdata; /* number of RRs in this set */
+ /* followed by encoded owner name, and then rdata */
+} dns_masterrawrdataset_t;
+
+/*
+ * Method prototype: a callback to register each include file as
+ * it is encountered.
+ */
+typedef void
+(*dns_masterincludecb_t)(const char *file, void *arg);
+
+/***
+ *** 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,
+ uint32_t resign,
+ dns_rdatacallbacks_t *callbacks,
+ isc_mem_t *mctx,
+ dns_masterformat_t format);
+
+isc_result_t
+dns_master_loadfile4(const char *master_file,
+ dns_name_t *top,
+ dns_name_t *origin,
+ dns_rdataclass_t zclass,
+ unsigned int options,
+ uint32_t resign,
+ dns_rdatacallbacks_t *callbacks,
+ dns_masterincludecb_t include_cb,
+ void *include_arg, isc_mem_t *mctx,
+ dns_masterformat_t format);
+
+isc_result_t
+dns_master_loadfile5(const char *master_file,
+ dns_name_t *top,
+ dns_name_t *origin,
+ dns_rdataclass_t zclass,
+ unsigned int options,
+ uint32_t resign,
+ dns_rdatacallbacks_t *callbacks,
+ dns_masterincludecb_t include_cb,
+ void *include_arg, isc_mem_t *mctx,
+ dns_masterformat_t format,
+ dns_ttl_t maxttl);
+
+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,
+ 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_loadfileinc4(const char *master_file,
+ dns_name_t *top,
+ dns_name_t *origin,
+ dns_rdataclass_t zclass,
+ unsigned int options,
+ uint32_t resign,
+ dns_rdatacallbacks_t *callbacks,
+ isc_task_t *task,
+ dns_loaddonefunc_t done, void *done_arg,
+ dns_loadctx_t **ctxp,
+ dns_masterincludecb_t include_cb, void *include_arg,
+ isc_mem_t *mctx, dns_masterformat_t format);
+
+isc_result_t
+dns_master_loadfileinc5(const char *master_file,
+ dns_name_t *top,
+ dns_name_t *origin,
+ dns_rdataclass_t zclass,
+ unsigned int options,
+ uint32_t resign,
+ dns_rdatacallbacks_t *callbacks,
+ isc_task_t *task,
+ dns_loaddonefunc_t done, void *done_arg,
+ dns_loadctx_t **ctxp,
+ dns_masterincludecb_t include_cb, void *include_arg,
+ isc_mem_t *mctx, dns_masterformat_t format,
+ uint32_t maxttl);
+
+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
+ */
+
+void
+dns_master_initrawheader(dns_masterrawheader_t *header);
+/*%<
+ * Initializes the header for a raw master file, setting all
+ * values to zero.
+ */
+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..b01ca08
--- /dev/null
+++ b/lib/dns/include/dns/masterdump.h
@@ -0,0 +1,428 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#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 0x000010000ULL
+
+/*%
+ * 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 0x000020000ULL
+
+/*% Omit the class when possible. */
+#define DNS_STYLEFLAG_OMIT_CLASS 0x000040000ULL
+
+/*% Output $TTL directives. */
+#define DNS_STYLEFLAG_TTL 0x000080000ULL
+
+/*%
+ * Output $ORIGIN directives and print owner names relative to
+ * the origin when possible.
+ */
+#define DNS_STYLEFLAG_REL_OWNER 0x000100000ULL
+
+/*% 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 0x000200000ULL
+
+/*% Print the trust level of each rdataset. */
+#define DNS_STYLEFLAG_TRUST 0x000400000ULL
+
+/*% Print negative caching entries. */
+#define DNS_STYLEFLAG_NCACHE 0x000800000ULL
+
+/*% Never print the TTL. */
+#define DNS_STYLEFLAG_NO_TTL 0x001000000ULL
+
+/*% Never print the CLASS. */
+#define DNS_STYLEFLAG_NO_CLASS 0x002000000ULL
+
+/*% Report re-signing time. */
+#define DNS_STYLEFLAG_RESIGN 0x004000000ULL
+
+/*% Don't printout the cryptographic parts of DNSSEC records. */
+#define DNS_STYLEFLAG_NOCRYPTO 0x008000000ULL
+
+/*% Comment out data by prepending with ";" */
+#define DNS_STYLEFLAG_COMMENTDATA 0x010000000ULL
+
+/*% Print TTL with human-readable units. */
+#define DNS_STYLEFLAG_TTL_UNITS 0x020000000ULL
+
+/*% Indent output. */
+#define DNS_STYLEFLAG_INDENT 0x040000000ULL
+
+/*% Output in YAML style. */
+#define DNS_STYLEFLAG_YAML 0x080000000ULL
+
+/*% Print ECS cache entries as comments (reserved for future use). */
+#define DNS_STYLEFLAG_ECSCACHE 0x100000000ULL
+
+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;
+
+/*%
+ * Similar to dns_master_style_debug but data is prepended with ";"
+ */
+LIBDNS_EXTERNAL_DATA extern const dns_master_style_t dns_master_style_comment;
+
+/*%
+ * Similar to dns_master_style_debug but data is indented with
+ * dns_master_indentstr (defaults to tab).
+ */
+LIBDNS_EXTERNAL_DATA extern const dns_master_style_t dns_master_style_indent;
+
+/*%
+ * The style used for dumping "key" zones.
+ */
+LIBDNS_EXTERNAL_DATA extern const dns_master_style_t dns_master_style_keyzone;
+
+/*%
+ * YAML-compatible output
+ */
+LIBDNS_EXTERNAL_DATA extern const dns_master_style_t dns_master_style_yaml;
+
+/*%
+ * The default indent string to prepend lines with when using
+ * styleflag DNS_STYLEFLAG_INDENT or DNS_STYLEFLAG_YAML.
+ * This is set to "\t" by default. The indent is repeated
+ * 'dns_master_indent' times. This precedes everything else
+ * on the line, including comment characters (;).
+ *
+ * XXX: Changing this value at runtime is not thread-safe.
+ */
+LIBDNS_EXTERNAL_DATA extern const char *dns_master_indentstr;
+
+/*%
+ * The number of copies of the indent string to put at the beginning
+ * of the line when using DNS_STYLEFLAG_INDENT or DNS_STYLEFLAG_YAML.
+ * This is set to 1 by default. It is increased and decreased
+ * to adjust indentation levels when producing YAML output.
+ *
+ * XXX: This is not thread-safe.
+ */
+LIBDNS_EXTERNAL_DATA extern unsigned int dns_master_indent;
+
+/***
+ *** 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);
+
+isc_result_t
+dns_master_dumptostream3(isc_mem_t *mctx, dns_db_t *db,
+ dns_dbversion_t *version,
+ const dns_master_style_t *style,
+ dns_masterformat_t format,
+ dns_masterrawheader_t *header, 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_dumptostream3(),
+ * which always specifies the dns_masterformat_text format.
+ * dns_master_dumptostream2() is an old form which always specifies
+ * a NULL header.
+ *
+ * If 'format' is dns_masterformat_raw, then 'header' can contain
+ * information to be written to the file header.
+ *
+ * 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_dumpinc3(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, dns_masterrawheader_t *header);
+
+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);
+
+isc_result_t
+dns_master_dump3(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, dns_masterrawheader_t *header);
+
+/*%<
+ * 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 _dumpinc3()
+ * and _dump3(), respectively, which always specify the dns_masterformat_text
+ * format. dns_master_dumpinc2() and dns_master_dump2() are old forms which
+ * always specify a NULL header.
+ *
+ * If 'format' is dns_masterformat_raw, then 'header' can contain
+ * information to be written to the file header.
+ *
+ * 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);
+
+dns_masterstyle_flags_t
+dns_master_styleflags(const dns_master_style_t *style);
+
+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);
+
+isc_result_t
+dns_master_stylecreate2(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,
+ unsigned int split_width, isc_mem_t *mctx);
+
+void
+dns_master_styledestroy(dns_master_style_t **style, isc_mem_t *mctx);
+
+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..334123b
--- /dev/null
+++ b/lib/dns/include/dns/message.h
@@ -0,0 +1,1436 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef DNS_MESSAGE_H
+#define DNS_MESSAGE_H 1
+
+/***
+ *** Imports
+ ***/
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#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_parse() 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, 0, 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 3 /*%< NSID opt code */
+#define DNS_OPT_CLIENT_SUBNET 8 /*%< client subnet opt code */
+#define DNS_OPT_EXPIRE 9 /*%< EXPIRE opt code */
+#define DNS_OPT_COOKIE 10 /*%< COOKIE opt code */
+#define DNS_OPT_PAD 12 /*%< PAD opt code */
+#define DNS_OPT_KEY_TAG 14 /*%< Key tag opt code */
+
+/*%< Experimental options [65001...65534] as per RFC6891 */
+
+/*%< The number of EDNS options we know about. */
+#define DNS_EDNSOPTIONS 5
+
+#define DNS_MESSAGE_REPLYPRESERVE (DNS_MESSAGEFLAG_RD|DNS_MESSAGEFLAG_CD)
+#define DNS_MESSAGEEXTFLAG_REPLYPRESERVE (DNS_MESSAGEEXTFLAG_DO)
+
+#define DNS_MESSAGE_HEADERLEN 12 /*%< 6 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
+#define DNS_MESSAGETEXTFLAG_ONESOA 0x0004
+#define DNS_MESSAGETEXTFLAG_OMITSOA 0x0008
+
+/*
+ * 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 /*%< truncation 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. */
+#ifdef ALLOW_FILTER_AAAA
+#define DNS_MESSAGERENDER_FILTER_AAAA 0x0020 /*%< filter AAAA records */
+#endif
+
+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;
+ dns_opcode_t 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 cc_ok : 1;
+ unsigned int cc_bad : 1;
+ unsigned int tkey : 1;
+ unsigned int rdclass_set : 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;
+};
+
+struct dns_ednsopt {
+ uint16_t code;
+ uint16_t length;
+ unsigned char *value;
+};
+
+/***
+ *** 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 on flags:
+ *\li If #DNS_MESSAGETEXTFLAG_NOCOMMENTS is cleared, lines beginning with
+ * ";;" will be emitted indicating section name.
+ *\li If #DNS_MESSAGETEXTFLAG_NOHEADERS is cleared, header lines will be
+ * emitted.
+ *\li If #DNS_MESSAGETEXTFLAG_ONESOA is set then only print the first
+ * SOA record in the answer section.
+ *\li If *#DNS_MESSAGETEXTFLAG_OMITSOA is set don't print any SOA records
+ * in the answer section.
+ *
+ * The SOA flags are useful for suppressing the display of the second
+ * SOA record in an AXFR by setting #DNS_MESSAGETEXTFLAG_ONESOA on the
+ * first message in an AXFR stream and #DNS_MESSAGETEXTFLAG_OMITSOA on
+ * subsequent messages.
+ *
+ * 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, bool 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 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
+ * dynamically 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.
+ */
+
+void
+dns_message_logpacket(dns_message_t *message, const char *description,
+ isc_logcategory_t *category, isc_logmodule_t *module,
+ int level, isc_mem_t *mctx);
+void
+dns_message_logpacket2(dns_message_t *message,
+ const char *description, isc_sockaddr_t *address,
+ isc_logcategory_t *category, isc_logmodule_t *module,
+ int level, isc_mem_t *mctx);
+void
+dns_message_logfmtpacket(dns_message_t *message, const char *description,
+ isc_logcategory_t *category, isc_logmodule_t *module,
+ const dns_master_style_t *style, int level,
+ isc_mem_t *mctx);
+void
+dns_message_logfmtpacket2(dns_message_t *message,
+ const char *description, isc_sockaddr_t *address,
+ isc_logcategory_t *category, isc_logmodule_t *module,
+ const dns_master_style_t *style, int level,
+ isc_mem_t *mctx);
+/*%<
+ * Log 'message' at the specified logging parameters.
+ *
+ * For dns_message_logpacket and dns_message_logfmtpacket expect the
+ * 'description' to end in a newline.
+ *
+ * For dns_message_logpacket2 and dns_message_logfmtpacket2
+ * 'description' will be emitted at the start of the message followed
+ * by the formatted address and a newline.
+ *
+ * Requires:
+ * \li message be a valid.
+ * \li description to be non NULL.
+ * \li address to be non NULL.
+ * \li category to be valid.
+ * \li module to be valid.
+ * \li style to be valid.
+ * \li mctx to be a valid.
+ */
+
+isc_result_t
+dns_message_buildopt(dns_message_t *msg, dns_rdataset_t **opt,
+ unsigned int version, uint16_t udpsize,
+ unsigned int flags, dns_ednsopt_t *ednsopts, size_t count);
+/*%<
+ * Built a opt record.
+ *
+ * Requires:
+ * \li msg be a valid message.
+ * \li opt to be a non NULL and *opt to be NULL.
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS on success.
+ * \li ISC_R_NOMEMORY
+ * \li ISC_R_NOSPACE
+ * \li other.
+ */
+
+void
+dns_message_setclass(dns_message_t *msg, dns_rdataclass_t rdclass);
+/*%<
+ * Set the expected class of records in the response.
+ *
+ * Requires:
+ * \li msg be a valid message with parsing intent.
+ */
+
+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..bbb663b
--- /dev/null
+++ b/lib/dns/include/dns/name.h
@@ -0,0 +1,1423 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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 <inttypes.h>
+#include <stdbool.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.
+ *****/
+
+/***
+ *** 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 0x00000001
+#define DNS_NAMEATTR_READONLY 0x00000002
+#define DNS_NAMEATTR_DYNAMIC 0x00000004
+#define DNS_NAMEATTR_DYNOFFSETS 0x00000008
+#define DNS_NAMEATTR_NOCOMPRESS 0x00000010
+/*
+ * Attributes below 0x0100 reserved for name.c usage.
+ */
+#define DNS_NAMEATTR_CACHE 0x00000100 /*%< Used by resolver. */
+#define DNS_NAMEATTR_ANSWER 0x00000200 /*%< Used by resolver. */
+#define DNS_NAMEATTR_NCACHE 0x00000400 /*%< Used by resolver. */
+#define DNS_NAMEATTR_CHAINING 0x00000800 /*%< Used by resolver. */
+#define DNS_NAMEATTR_CHASE 0x00001000 /*%< Used by resolver. */
+#define DNS_NAMEATTR_WILDCARD 0x00002000 /*%< Used by server. */
+#define DNS_NAMEATTR_PREREQUISITE 0x00004000 /*%< Used by client. */
+#define DNS_NAMEATTR_UPDATE 0x00008000 /*%< Used by client. */
+#define DNS_NAMEATTR_HASUPDATEREC 0x00010000 /*%< Used by client. */
+
+/*
+ * Various flags.
+ */
+#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;
+
+/*%<
+ * DNS_NAME_INITNONABSOLUTE and DNS_NAME_INITABSOLUTE are macros for
+ * initializing dns_name_t structures.
+ *
+ * Note[1]: 'length' is set to (sizeof(A) - 1) in DNS_NAME_INITNONABSOLUTE
+ * and sizeof(A) in DNS_NAME_INITABSOLUTE to allow C strings to be used
+ * to initialize 'ndata'.
+ *
+ * Note[2]: The final value of offsets for DNS_NAME_INITABSOLUTE should
+ * match (sizeof(A) - 1) which is the offset of the root label.
+ *
+ * Typical usage:
+ * unsigned char data[] = "\005value";
+ * unsigned char offsets[] = { 0 };
+ * dns_name_t value = DNS_NAME_INITNONABSOLUTE(data, offsets);
+ *
+ * unsigned char data[] = "\005value";
+ * unsigned char offsets[] = { 0, 6 };
+ * dns_name_t value = DNS_NAME_INITABSOLUTE(data, offsets);
+ */
+#define DNS_NAME_INITNONABSOLUTE(A,B) { \
+ DNS_NAME_MAGIC, \
+ A, (sizeof(A) - 1), sizeof(B), \
+ DNS_NAMEATTR_READONLY, \
+ B, NULL, { (void *)-1, (void *)-1}, \
+ {NULL, NULL} \
+}
+
+#define DNS_NAME_INITABSOLUTE(A,B) { \
+ DNS_NAME_MAGIC, \
+ A, sizeof(A), sizeof(B), \
+ DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE, \
+ B, NULL, { (void *)-1, (void *)-1}, \
+ {NULL, NULL} \
+}
+
+/*%
+ * 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,
+ bool 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) == 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) == 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.
+ */
+
+bool
+dns_name_isvalid(const dns_name_t *name);
+/*%<
+ * Check whether 'name' points to a valid dns_name
+ */
+
+/***
+ *** 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_concatenate() 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.
+ */
+
+bool
+dns_name_hasbuffer(const dns_name_t *name);
+/*%<
+ * Does 'name' have a dedicated buffer?
+ *
+ * Requires:
+ * \li 'name' is a valid name.
+ *
+ * Returns:
+ * \li true 'name' has a dedicated buffer.
+ * \li false 'name' does not have a dedicated buffer.
+ */
+
+/***
+ *** Properties
+ ***/
+
+bool
+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.
+ */
+
+bool
+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, bool case_sensitive);
+/*%<
+ * Provide a hash value for 'name'.
+ *
+ * Note: if 'case_sensitive' is 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, bool 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 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, bool case_sensitive);
+/*%<
+ * Provide a hash value for 'name', where the hash value is the sum
+ * of the hash values of each label. This function should only be used
+ * when incremental hashing is necessary, for example, during RBT
+ * traversal. It is not currently used in BIND. Generally,
+ * dns_name_fullhash() is the correct function to use for name
+ * hashing.
+ *
+ * Note: if 'case_sensitive' is 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'
+ */
+
+bool
+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 true 'name1' and 'name2' are equal
+ * \li false 'name1' and 'name2' are not equal
+ */
+
+bool
+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'
+ */
+
+bool
+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'
+ */
+
+bool
+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,
+ const 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_BADDOTTEDQUAD
+ *\li #ISC_R_NOSPACE
+ *\li #ISC_R_UNEXPECTEDEND
+ */
+
+#define DNS_NAME_OMITFINALDOT 0x01U
+#define DNS_NAME_MASTERFILE 0x02U /* escape $ and @ */
+
+isc_result_t
+dns_name_toprincipal(const dns_name_t *name, isc_buffer_t *target);
+
+isc_result_t
+dns_name_totext(const dns_name_t *name, bool omit_final_dot,
+ isc_buffer_t *target);
+
+isc_result_t
+dns_name_totext2(const dns_name_t *name, unsigned int options,
+ 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_OMITFINALDOT is set in options, then the final '.'
+ * in absolute names other than the root name will be omitted.
+ *
+ *\li If DNS_NAME_MASTERFILE is set in options, '$' and '@' will also
+ * be escaped.
+ *
+ *\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, bool 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.
+ *
+ * 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.
+ *
+ */
+
+bool
+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 'true' if the name is dynamic otherwise '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(const 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_tostring(dns_name_t *source, char **target, isc_mem_t *mctx);
+/*%<
+ * Convert 'name' to string format, allocating sufficient memory to
+ * hold it (free with isc_mem_free()).
+ *
+ * Differs from dns_name_format in that it allocates its own memory.
+ *
+ * Requires:
+ *
+ *\li 'name' is a valid name.
+ *\li 'target' is not NULL.
+ *\li '*target' is NULL.
+ *
+ * Returns:
+ *
+ *\li ISC_R_SUCCESS
+ *\li ISC_R_NOMEMORY
+ *
+ *\li Any error that dns_name_totext() can return.
+ */
+
+isc_result_t
+dns_name_fromstring(dns_name_t *target, const char *src, unsigned int options,
+ isc_mem_t *mctx);
+isc_result_t
+dns_name_fromstring2(dns_name_t *target, const char *src,
+ const dns_name_t *origin, unsigned int options,
+ isc_mem_t *mctx);
+/*%<
+ * Convert a string to a name and place it in target, allocating memory
+ * as necessary. 'options' has the same semantics as that of
+ * dns_name_fromtext().
+ *
+ * If 'target' has a buffer then the name will be copied into it rather than
+ * memory being allocated.
+ *
+ * Requires:
+ *
+ * \li 'target' is a valid name that is not read-only.
+ * \li 'src' is not NULL.
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS
+ *
+ *\li Any error that dns_name_fromtext() can return.
+ *
+ *\li Any error that dns_name_dup() can return.
+ */
+
+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(const 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
+ */
+
+bool
+dns_name_ishostname(const dns_name_t *name, bool wildcard);
+/*%<
+ * Return if 'name' is a valid hostname. RFC 952 / RFC 1123.
+ * If 'wildcard' is true then allow the first label of name to
+ * be a wildcard.
+ * The root is also accepted.
+ *
+ * Requires:
+ * 'name' to be valid.
+ */
+
+
+bool
+dns_name_ismailbox(const dns_name_t *name);
+/*%<
+ * Return if 'name' is a valid mailbox. RFC 821.
+ *
+ * Requires:
+ * \li 'name' to be valid.
+ */
+
+bool
+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();
+ */
+
+bool
+dns_name_isdnssd(const dns_name_t *owner);
+/*%<
+ * Determine if the 'owner' is a DNS-SD prefix.
+ */
+
+bool
+dns_name_isrfc1918(const dns_name_t *owner);
+/*%<
+ * Determine if the 'name' is in the RFC 1918 reverse namespace.
+ */
+
+bool
+dns_name_isula(const dns_name_t *owner);
+/*%<
+ * Determine if the 'name' is in the ULA reverse namespace.
+ */
+
+bool
+dns_name_istat(const dns_name_t *name);
+/*
+ * Determine if 'name' is a potential 'trust-anchor-telemetry' name.
+ */
+
+ISC_LANG_ENDDECLS
+
+/*
+ *** High Performance 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 { \
+ dns_name_t *_n = (n); \
+ /* memset(_n, 0, sizeof(*_n)); */ \
+ _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 ? true : 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..2942c26
--- /dev/null
+++ b/lib/dns/include/dns/ncache.h
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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 <stdbool.h>
+
+#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,
+ bool 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'.
+ *
+ * \li dns_ncache_add produces a negative cache entry with a trust of no
+ * more than answer
+ * \li dns_ncache_addoptout produces a negative cache entry which will have
+ * a trust of secure if all the records that make up the entry are secure.
+ *
+ * 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_RDATASETATTR_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.
+ *
+ */
+
+isc_result_t
+dns_ncache_getsigrdataset(dns_rdataset_t *ncacherdataset, dns_name_t *name,
+ dns_rdatatype_t covers, dns_rdataset_t *rdataset);
+/*%<
+ * Similar to dns_ncache_getrdataset() but get the rrsig that matches.
+ */
+
+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..18cec17
--- /dev/null
+++ b/lib/dns/include/dns/nsec.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#ifndef DNS_NSEC_H
+#define DNS_NSEC_H 1
+
+/*! \file dns/nsec.h */
+
+#include <stdbool.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.
+ */
+
+bool
+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,
+ bool *answer);
+/*
+ * Report whether the DNSKEY RRset has a NSEC only algorithm. Unknown
+ * algorithms are assumed to support NSEC3. If DNSKEY is not found,
+ * *answer is set to false, and ISC_R_NOTFOUND is returned.
+ *
+ * Requires:
+ * 'answer' to be non NULL.
+ */
+
+unsigned int
+dns_nsec_compressbitmap(unsigned char *map, const unsigned char *raw,
+ unsigned int max_type);
+/*%<
+ * Convert a raw bitmap into a compressed windowed bit map. 'map' and 'raw'
+ * may overlap.
+ *
+ * Returns the length of the compressed windowed bit map.
+ */
+
+void
+dns_nsec_setbit(unsigned char *array, unsigned int type, unsigned int bit);
+/*%<
+ * Set type bit in raw 'array' to 'bit'.
+ */
+
+bool
+dns_nsec_isset(const unsigned char *array, unsigned int type);
+/*%<
+ * Test if the corresponding 'type' bit is set in 'array'.
+ */
+
+isc_result_t
+dns_nsec_noexistnodata(dns_rdatatype_t type, dns_name_t *name,
+ dns_name_t *nsecname, dns_rdataset_t *nsecset,
+ bool *exists, bool *data,
+ dns_name_t *wild, dns_nseclog_t log, void *arg);
+/*%
+ * 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.
+ */
+
+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..07d2bf0
--- /dev/null
+++ b/lib/dns/include/dns/nsec3.h
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#ifndef DNS_NSEC3_H
+#define DNS_NSEC3_H 1
+
+#include <stdbool.h>
+
+#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>
+
+#define DNS_NSEC3_SALTSIZE 255
+
+/*
+ * 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 ((dns_hash_t)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'.
+ */
+
+bool
+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.
+ */
+
+bool
+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, bool 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,
+ bool unsecure, dns_diff_t *diff);
+
+isc_result_t
+dns_nsec3_addnsec3sx(dns_db_t *db, dns_dbversion_t *version,
+ dns_name_t *name, dns_ttl_t nsecttl,
+ bool unsecure, dns_rdatatype_t private,
+ 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 chosen 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.
+ *
+ * dns_nsec3_addnsec3sx() is similar to dns_nsec3_addnsec3s() but 'private'
+ * specifies the type of the private rdataset to be checked in addition to
+ * the nsec3param rdataset at the zone apex.
+ *
+ * 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);
+
+isc_result_t
+dns_nsec3_delnsec3sx(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name,
+ dns_rdatatype_t private, 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.
+ *
+ * dns_nsec3_delnsec3sx() is similar to dns_nsec3_delnsec3s() but 'private'
+ * specifies the type of the private rdataset to be checked in addition to
+ * the nsec3param rdataset at the zone apex.
+ *
+ * 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,
+ bool complete, bool *answer);
+
+isc_result_t
+dns_nsec3_activex(dns_db_t *db, dns_dbversion_t *version,
+ bool complete, dns_rdatatype_t private,
+ bool *answer);
+/*%<
+ * Check if there are any complete/to be built NSEC3 chains.
+ * If 'complete' is true only complete chains will be recognized.
+ *
+ * dns_nsec3_activex() is similar to dns_nsec3_active() but 'private'
+ * specifies the type of the private rdataset to be checked in addition to
+ * the nsec3param rdataset at the zone apex.
+ *
+ * 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.
+ */
+
+bool
+dns_nsec3param_fromprivate(dns_rdata_t *src, dns_rdata_t *target,
+ unsigned char *buf, size_t buflen);
+/*%<
+ * Convert a private rdata to a nsec3param rdata.
+ *
+ * Return true if 'src' could be successfully converted.
+ *
+ * 'buf' should be at least DNS_NSEC3PARAM_BUFFERSIZE in size.
+ */
+
+void
+dns_nsec3param_toprivate(dns_rdata_t *src, dns_rdata_t *target,
+ dns_rdatatype_t privatetype,
+ unsigned char *buf, size_t buflen);
+/*%<
+ * Convert a nsec3param rdata to a private rdata.
+ *
+ * 'buf' should be at least src->length + 1 in size.
+ */
+
+isc_result_t
+dns_nsec3param_salttotext(dns_rdata_nsec3param_t *nsec3param, char *dst,
+ size_t dstlen);
+/*%<
+ * Convert the salt of given NSEC3PARAM RDATA into hex-encoded, NULL-terminated
+ * text stored at "dst".
+ *
+ * Requires:
+ *
+ *\li "dst" to have enough space (as indicated by "dstlen") to hold the
+ * resulting text and its NULL-terminating byte.
+ */
+
+isc_result_t
+dns_nsec3param_deletechains(dns_db_t *db, dns_dbversion_t *ver,
+ dns_zone_t *zone, bool nonsec,
+ dns_diff_t *diff);
+
+/*%<
+ * Mark NSEC3PARAM for deletion.
+ */
+
+isc_result_t
+dns_nsec3_noexistnodata(dns_rdatatype_t type, dns_name_t* name,
+ dns_name_t *nsec3name, dns_rdataset_t *nsec3set,
+ dns_name_t *zonename, bool *exists,
+ bool *data, bool *optout,
+ bool *unknown, bool *setclosest,
+ bool *setnearest, dns_name_t *closest,
+ dns_name_t *nearest, dns_nseclog_t logit, void *arg);
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_NSEC3_H */
diff --git a/lib/dns/include/dns/nta.h b/lib/dns/include/dns/nta.h
new file mode 100644
index 0000000..edbaa77
--- /dev/null
+++ b/lib/dns/include/dns/nta.h
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef DNS_NTA_H
+#define DNS_NTA_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file
+ * \brief
+ * The NTA module provides services for storing and retrieving negative
+ * trust anchors, and determine whether a given domain is subject to
+ * DNSSEC validation.
+ */
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/buffer.h>
+#include <isc/lang.h>
+#include <isc/magic.h>
+#include <isc/rwlock.h>
+#include <isc/stdtime.h>
+#include <isc/task.h>
+#include <isc/timer.h>
+
+#include <dns/types.h>
+#include <dns/rdataset.h>
+#include <dns/resolver.h>
+#include <dns/view.h>
+
+ISC_LANG_BEGINDECLS
+
+struct dns_ntatable {
+ /* Unlocked. */
+ unsigned int magic;
+ dns_view_t *view;
+ isc_rwlock_t rwlock;
+ isc_taskmgr_t *taskmgr;
+ isc_timermgr_t *timermgr;
+ isc_task_t *task;
+ /* Locked by rwlock. */
+ uint32_t references;
+ dns_rbt_t *table;
+};
+
+#define NTATABLE_MAGIC ISC_MAGIC('N', 'T', 'A', 't')
+#define VALID_NTATABLE(nt) ISC_MAGIC_VALID(nt, NTATABLE_MAGIC)
+
+isc_result_t
+dns_ntatable_create(dns_view_t *view,
+ isc_taskmgr_t *taskmgr, isc_timermgr_t *timermgr,
+ dns_ntatable_t **ntatablep);
+/*%<
+ * Create an NTA table in view 'view'.
+ *
+ * Requires:
+ *
+ *\li 'view' is a valid view.
+ *
+ *\li 'tmgr' is a valid timer manager.
+ *
+ *\li ntatablep != NULL && *ntatablep == NULL
+ *
+ * Ensures:
+ *
+ *\li On success, *ntatablep is a valid, empty NTA table.
+ *
+ * Returns:
+ *
+ *\li ISC_R_SUCCESS
+ *\li Any other result indicates failure.
+ */
+
+void
+dns_ntatable_attach(dns_ntatable_t *source, dns_ntatable_t **targetp);
+/*%<
+ * Attach *targetp to source.
+ *
+ * Requires:
+ *
+ *\li 'source' is a valid ntatable.
+ *
+ *\li 'targetp' points to a NULL dns_ntatable_t *.
+ *
+ * Ensures:
+ *
+ *\li *targetp is attached to source.
+ */
+
+void
+dns_ntatable_detach(dns_ntatable_t **ntatablep);
+/*%<
+ * Detach *ntatablep from its ntatable.
+ *
+ * Requires:
+ *
+ *\li 'ntatablep' points to a valid ntatable.
+ *
+ * Ensures:
+ *
+ *\li *ntatablep is NULL.
+ *
+ *\li If '*ntatablep' is the last reference to the ntatable,
+ * all resources used by the ntatable will be freed
+ */
+
+isc_result_t
+dns_ntatable_add(dns_ntatable_t *ntatable, dns_name_t *name,
+ bool force, isc_stdtime_t now,
+ uint32_t lifetime);
+/*%<
+ * Add a negative trust anchor to 'ntatable' for name 'name',
+ * which will expire at time 'now' + 'lifetime'. If 'force' is false,
+ * then the name will be checked periodically to see if it's bogus;
+ * if not, then the NTA will be allowed to expire early.
+ *
+ * Notes:
+ *
+ *\li If an NTA already exists in the table, its expiry time
+ * is updated.
+ *
+ * Requires:
+ *
+ *\li 'ntatable' points to a valid ntatable.
+ *
+ *\li 'name' points to a valid name.
+ *
+ * Returns:
+ *
+ *\li ISC_R_SUCCESS
+ *
+ *\li Any other result indicates failure.
+ */
+
+isc_result_t
+dns_ntatable_delete(dns_ntatable_t *ntatable, dns_name_t *keyname);
+/*%<
+ * Delete node(s) from 'ntatable' matching name 'keyname'
+ *
+ * Requires:
+ *
+ *\li 'ntatable' points to a valid ntatable.
+ *
+ *\li 'name' is not NULL
+ *
+ * Returns:
+ *
+ *\li ISC_R_SUCCESS
+ *
+ *\li Any other result indicates failure.
+ */
+
+bool
+dns_ntatable_covered(dns_ntatable_t *ntatable, isc_stdtime_t now,
+ dns_name_t *name, dns_name_t *anchor);
+/*%<
+ * Return true if 'name' is below a non-expired negative trust
+ * anchor which in turn is at or below 'anchor'.
+ *
+ * If 'ntatable' has not been initialized, return false.
+ *
+ * Requires:
+ *
+ *\li 'ntatable' is NULL or is a valid ntatable.
+ *
+ *\li 'name' is a valid absolute name.
+ */
+
+isc_result_t
+dns_ntatable_totext(dns_ntatable_t *ntatable, isc_buffer_t **buf);
+/*%<
+ * Dump the NTA table to buffer at 'buf'
+ *
+ * Requires:
+ * \li "ntatable" is a valid table.
+ *
+ * \li "*buf" is a valid buffer.
+ */
+
+isc_result_t
+dns_ntatable_dump(dns_ntatable_t *ntatable, FILE *fp);
+/*%<
+ * Dump the NTA table to the file opened as 'fp'.
+ */
+
+isc_result_t
+dns_ntatable_save(dns_ntatable_t *ntatable, FILE *fp);
+/*%<
+ * Save the NTA table to the file opened as 'fp', for later loading.
+ */
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_NTA_H */
diff --git a/lib/dns/include/dns/opcode.h b/lib/dns/include/dns/opcode.h
new file mode 100644
index 0000000..ff9e82e
--- /dev/null
+++ b/lib/dns/include/dns/opcode.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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..e7c0516
--- /dev/null
+++ b/lib/dns/include/dns/order.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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_RDATASETATTR_RANDOMIZE,
+ * #DNS_RDATASETATTR_FIXEDORDER or zero (#DNS_RDATASETATTR_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..58a1354
--- /dev/null
+++ b/lib/dns/include/dns/peer.h
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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 <inttypes.h>
+#include <stdbool.h>
+
+#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;
+ uint32_t refs;
+
+ isc_mem_t *mem;
+
+ ISC_LIST(dns_peer_t) elements;
+};
+
+struct dns_peer {
+ unsigned int magic;
+ uint32_t refs;
+
+ isc_mem_t *mem;
+
+ isc_netaddr_t address;
+ unsigned int prefixlen;
+ bool bogus;
+ dns_transfer_format_t transfer_format;
+ uint32_t transfers;
+ bool support_ixfr;
+ bool provide_ixfr;
+ bool request_ixfr;
+ bool support_edns;
+ bool request_nsid;
+ bool send_cookie;
+ bool request_expire;
+ bool force_tcp;
+ dns_name_t *key;
+ isc_sockaddr_t *transfer_source;
+ isc_dscp_t transfer_dscp;
+ isc_sockaddr_t *notify_source;
+ isc_dscp_t notify_dscp;
+ isc_sockaddr_t *query_source;
+ isc_dscp_t query_dscp;
+ uint16_t udpsize; /* receive size */
+ uint16_t maxudp; /* transmit size */
+ uint8_t ednsversion; /* edns version */
+
+ 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, bool newval);
+
+isc_result_t
+dns_peer_getbogus(dns_peer_t *peer, bool *retval);
+
+isc_result_t
+dns_peer_setrequestixfr(dns_peer_t *peer, bool newval);
+
+isc_result_t
+dns_peer_getrequestixfr(dns_peer_t *peer, bool *retval);
+
+isc_result_t
+dns_peer_setprovideixfr(dns_peer_t *peer, bool newval);
+
+isc_result_t
+dns_peer_getprovideixfr(dns_peer_t *peer, bool *retval);
+
+isc_result_t
+dns_peer_setrequestnsid(dns_peer_t *peer, bool newval);
+
+isc_result_t
+dns_peer_getrequestnsid(dns_peer_t *peer, bool *retval);
+
+isc_result_t
+dns_peer_setsendcookie(dns_peer_t *peer, bool newval);
+
+isc_result_t
+dns_peer_getsendcookie(dns_peer_t *peer, bool *retval);
+
+isc_result_t
+dns_peer_setrequestexpire(dns_peer_t *peer, bool newval);
+
+isc_result_t
+dns_peer_getrequestexpire(dns_peer_t *peer, bool *retval);
+
+isc_result_t
+dns_peer_setsupportedns(dns_peer_t *peer, bool newval);
+
+isc_result_t
+dns_peer_getforcetcp(dns_peer_t *peer, bool *retval);
+
+isc_result_t
+dns_peer_setforcetcp(dns_peer_t *peer, bool newval);
+
+isc_result_t
+dns_peer_getsupportedns(dns_peer_t *peer, bool *retval);
+
+isc_result_t
+dns_peer_settransfers(dns_peer_t *peer, uint32_t newval);
+
+isc_result_t
+dns_peer_gettransfers(dns_peer_t *peer, 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, uint16_t udpsize);
+
+isc_result_t
+dns_peer_getudpsize(dns_peer_t *peer, uint16_t *udpsize);
+
+isc_result_t
+dns_peer_setmaxudp(dns_peer_t *peer, uint16_t maxudp);
+
+isc_result_t
+dns_peer_getmaxudp(dns_peer_t *peer, 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_result_t
+dns_peer_setnotifydscp(dns_peer_t *peer, isc_dscp_t dscp);
+
+isc_result_t
+dns_peer_getnotifydscp(dns_peer_t *peer, isc_dscp_t *dscpp);
+
+isc_result_t
+dns_peer_settransferdscp(dns_peer_t *peer, isc_dscp_t dscp);
+
+isc_result_t
+dns_peer_gettransferdscp(dns_peer_t *peer, isc_dscp_t *dscpp);
+
+isc_result_t
+dns_peer_setquerydscp(dns_peer_t *peer, isc_dscp_t dscp);
+
+isc_result_t
+dns_peer_getquerydscp(dns_peer_t *peer, isc_dscp_t *dscpp);
+
+isc_result_t
+dns_peer_setednsversion(dns_peer_t *peer, uint8_t ednsversion);
+
+isc_result_t
+dns_peer_getednsversion(dns_peer_t *peer, uint8_t *ednsversion);
+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..471db97
--- /dev/null
+++ b/lib/dns/include/dns/portlist.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file dns/portlist.h */
+
+#include <stdbool.h>
+
+#include <isc/lang.h>
+#include <isc/net.h>
+#include <isc/types.h>
+
+#include <dns/types.h>
+
+#ifndef DNS_PORTLIST_H
+#define DNS_PORTLIST_H 1
+
+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
+ */
+
+bool
+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 #true if the tuple is found, 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
+
+#endif /* DNS_PORTLIST_H */
diff --git a/lib/dns/include/dns/private.h b/lib/dns/include/dns/private.h
new file mode 100644
index 0000000..8604746
--- /dev/null
+++ b/lib/dns/include/dns/private.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <stdbool.h>
+
+#include <isc/lang.h>
+#include <isc/types.h>
+
+#include <dns/types.h>
+#include <dns/db.h>
+
+#ifndef DNS_PRIVATE_H
+#define DNS_PRIVATE_H
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+dns_private_chains(dns_db_t *db, dns_dbversion_t *ver,
+ dns_rdatatype_t privatetype,
+ bool *build_nsec, bool *build_nsec3);
+/*%<
+ * Examine the NSEC, NSEC3PARAM and privatetype RRsets at the apex of the
+ * database to determine which of NSEC or NSEC3 chains we are currently
+ * maintaining. In normal operations only one of NSEC or NSEC3 is being
+ * maintained but when we are transitiong between NSEC and NSEC3 we need
+ * to update both sets of chains. If 'privatetype' is zero then the
+ * privatetype RRset will not be examined.
+ *
+ * Requires:
+ * \li 'db' is valid.
+ * \li 'version' is valid or NULL.
+ * \li 'build_nsec' is a pointer to a bool or NULL.
+ * \li 'build_nsec3' is a pointer to a bool or NULL.
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS, 'build_nsec' and 'build_nsec3' will be valid.
+ * \li other on error
+ */
+
+isc_result_t
+dns_private_totext(dns_rdata_t *privaterdata, isc_buffer_t *buffer);
+/*%<
+ * Convert a private-type RR 'privaterdata' to human-readable form,
+ * and place the result in 'buffer'. The text should indicate
+ * which action the private-type record specifies and whether the
+ * action has been completed.
+ *
+ * Requires:
+ * \li 'privaterdata' is a valid rdata containing at least five bytes
+ * \li 'buffer' is a valid buffer
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS
+ * \li other on error
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif
diff --git a/lib/dns/include/dns/rbt.h b/lib/dns/include/dns/rbt.h
new file mode 100644
index 0000000..67ac3e4
--- /dev/null
+++ b/lib/dns/include/dns/rbt.h
@@ -0,0 +1,1144 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#ifndef DNS_RBT_H
+#define DNS_RBT_H 1
+
+/*! \file dns/rbt.h */
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/assertions.h>
+#include <isc/crc64.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
+
+#define DNS_RBT_USEMAGIC 1
+
+/*
+ * 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) 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;
+enum {
+ DNS_RBT_NSEC_NORMAL=0, /* in main tree */
+ DNS_RBT_NSEC_HAS_NSEC=1, /* also has node in nsec tree */
+ DNS_RBT_NSEC_NSEC=2, /* in nsec tree */
+ DNS_RBT_NSEC_NSEC3=3 /* in nsec3 tree */
+};
+struct dns_rbtnode {
+#if DNS_RBT_USEMAGIC
+ unsigned int magic;
+#endif
+ /*@{*/
+ /*!
+ * 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 accommodate
+ * possible future expansion.
+ *
+ * In each case below the "range" indicated is what's _necessary_ for
+ * the bitfield to hold, not what it actually _can_ hold.
+ *
+ * Note: Tree lock must be held before modifying these
+ * bit-fields.
+ *
+ * Note: The two "unsigned int :0;" unnamed bitfields on either
+ * side of the bitfields below are scaffolding that border the
+ * set of bitfields which are accessed after acquiring the tree
+ * lock. Please don't insert any other bitfield members between
+ * the unnamed bitfields unless they should also be accessed
+ * after acquiring the tree lock.
+ */
+ unsigned int :0; /* start of bitfields c/o tree lock */
+ 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 nsec : 2; /*%< range is 0..3 */
+ unsigned int namelen : 8; /*%< range is 1..255 */
+ unsigned int offsetlen : 8; /*%< range is 1..128 */
+ unsigned int oldnamelen : 8; /*%< range is 1..255 */
+ /*@}*/
+
+ /* flags needed for serialization to file*/
+ unsigned int is_mmapped : 1;
+ unsigned int parent_is_relative : 1;
+ unsigned int left_is_relative : 1;
+ unsigned int right_is_relative : 1;
+ unsigned int down_is_relative : 1;
+ unsigned int data_is_relative : 1;
+
+ /* node needs to be cleaned from rpz */
+ unsigned int rpz : 1;
+ unsigned int :0; /* end of bitfields c/o tree lock */
+
+#ifdef DNS_RBT_USEHASH
+ unsigned int hashval;
+ dns_rbtnode_t *uppernode;
+ dns_rbtnode_t *hashnext;
+#endif
+ dns_rbtnode_t *parent;
+ dns_rbtnode_t *left;
+ dns_rbtnode_t *right;
+ dns_rbtnode_t *down;
+
+ /*%
+ * 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;
+
+ /*@{*/
+ /*!
+ * These values are used in the RBT DB implementation. The appropriate
+ * node lock must be held before accessing them.
+ *
+ * Note: The two "unsigned int :0;" unnamed bitfields on either
+ * side of the bitfields below are scaffolding that border the
+ * set of bitfields which are accessed after acquiring the node
+ * lock. Please don't insert any other bitfield members between
+ * the unnamed bitfields unless they should also be accessed
+ * after acquiring the node lock.
+ *
+ * NOTE: Do not merge these fields into bitfields above, as
+ * they'll all be put in the same qword that could be accessed
+ * without the node lock as it shares the qword with other
+ * members. Leave these members here so that they occupy a
+ * separate region of memory.
+ */
+ void *data;
+ unsigned int :0; /* start of bitfields c/o node lock */
+ unsigned int dirty:1;
+ unsigned int wild:1;
+ unsigned int locknum:DNS_RBT_LOCKLENGTH;
+#ifndef DNS_RBT_USEISCREFCOUNT
+ unsigned int references:DNS_RBT_REFLENGTH;
+#endif
+ unsigned int :0; /* end of bitfields c/o node lock */
+#ifdef DNS_RBT_USEISCREFCOUNT
+ 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);
+
+typedef isc_result_t (*dns_rbtdatawriter_t)(FILE *file,
+ unsigned char *data,
+ void *arg,
+ uint64_t *crc);
+
+typedef isc_result_t (*dns_rbtdatafixer_t)(dns_rbtnode_t *rbtnode,
+ void *base, size_t offset,
+ void *arg, uint64_t *crc);
+
+typedef void (*dns_rbtdeleter_t)(void *, void *);
+
+/*****
+ ***** 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.
+ *
+ * Chains may be used to iterate over a tree of trees. After setting up the
+ * chain's structure using dns_rbtnodechain_init(), it needs to be initialized
+ * to point to the lexically first or lexically last node in the tree of trees
+ * using dns_rbtnodechain_first() or dns_rbtnodechain_last(), respectively.
+ * Calling dns_rbtnodechain_next() or dns_rbtnodechain_prev() then moves the
+ * chain over to the next or previous node, respectively.
+ *
+ * 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 targeted.
+ *
+ * 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, dns_rbtdeleter_t deleter,
+ 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, const 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, const 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 arising 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 mutually
+ * 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, bool recurse);
+/*%<
+ * Delete 'name' from the tree of trees.
+ *
+ * Notes:
+ *\li When 'name' is removed, if recurse is 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, bool recurse);
+/*%<
+ * Delete 'node' from the tree of trees.
+ *
+ * Notes:
+ *\li When 'node' is removed, if recurse is 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 when
+ * 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.
+ */
+
+size_t
+dns_rbt_hashsize(dns_rbt_t *rbt);
+/*%<
+ * Obtain the current number of buckets in the 'rbt' hash table.
+ *
+ * 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.
+ */
+
+off_t
+dns_rbt_serialize_align(off_t target);
+/*%<
+ * Align the provided integer to a pointer-size boundary.
+ * This should be used if, during serialization of data to a will-be
+ * mmap()ed file, a pointer alignment is needed for some data.
+ */
+
+isc_result_t
+dns_rbt_serialize_tree(FILE *file, dns_rbt_t *rbt,
+ dns_rbtdatawriter_t datawriter,
+ void *writer_arg, off_t *offset);
+/*%<
+ * Write out the RBT structure and its data to a file.
+ *
+ * Notes:
+ * \li The file must be an actual file which allows seek() calls, so it cannot
+ * be a stream. Returns ISC_R_INVALIDFILE if not.
+ */
+
+isc_result_t
+dns_rbt_deserialize_tree(void *base_address, size_t filesize,
+ off_t header_offset, isc_mem_t *mctx,
+ dns_rbtdeleter_t deleter, void *deleter_arg,
+ dns_rbtdatafixer_t datafixer, void *fixer_arg,
+ dns_rbtnode_t **originp, dns_rbt_t **rbtp);
+/*%<
+ * Read a RBT structure and its data from a file.
+ *
+ * If 'originp' is not NULL, then it is pointed to the root node of the RBT.
+ *
+ * Notes:
+ * \li The file must be an actual file which allows seek() calls, so it cannot
+ * be a stream. This condition is not checked in the code.
+ */
+
+void
+dns_rbt_printtext(dns_rbt_t *rbt,
+ void (*data_printer)(FILE *, void *), FILE *f);
+/*%<
+ * Print an ASCII representation of the internal structure of the red-black
+ * tree of trees to the passed stream.
+ *
+ * data_printer is a callback function that is called to print the data
+ * in a node. It should print it to the passed FILE stream.
+ *
+ * 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.
+ */
+
+void
+dns_rbt_printdot(dns_rbt_t *rbt, bool show_pointers, FILE *f);
+/*%<
+ * Print a GraphViz dot representation of the internal structure of the
+ * red-black tree of trees to the passed stream.
+ *
+ * If show_pointers is TRUE, pointers are also included in the generated
+ * graph.
+ *
+ * Notes:
+ * \li The name stored at each node, along with the node's color is displayed.
+ * Then the down pointer, left and right pointers are displayed
+ * recursively in turn. NULL left, right and down pointers are
+ * silently omitted.
+ */
+
+void
+dns_rbt_printnodeinfo(dns_rbtnode_t *n, FILE *f);
+/*%<
+ * Print out various information about a node
+ *
+ * Requires:
+ *\li 'n' is a valid pointer.
+ *
+ *\li 'f' points to a valid open FILE structure that allows writing.
+ */
+
+
+size_t
+dns__rbt_getheight(dns_rbt_t *rbt);
+/*%<
+ * Return the maximum height of sub-root nodes found in the red-black
+ * forest.
+ *
+ * The height of a node is defined as the number of nodes in the longest
+ * path from the node to a leaf. For each subtree in the forest, this
+ * function determines the height of its root node. Then it returns the
+ * maximum such height in the forest.
+ *
+ * Note: This function exists for testing purposes. Non-test code must
+ * not use it.
+ *
+ * Requires:
+ * \li rbt is a valid rbt manager.
+ */
+
+bool
+dns__rbt_checkproperties(dns_rbt_t *rbt);
+/*%<
+ * Check red-black properties of the forest.
+ *
+ * Note: This function exists for testing purposes. Non-test code must
+ * not use it.
+ *
+ * Requires:
+ * \li rbt is a valid rbt manager.
+ */
+
+size_t
+dns__rbtnode_getdistance(dns_rbtnode_t *node);
+/*%<
+ * Return the distance (in nodes) from the node to its upper node of its
+ * subtree. The root node has a distance of 1. A child of the root node
+ * has a distance of 2.
+ */
+
+/*****
+ ***** 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);
+/*%<
+ * Descend 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) ISC_REQUIRE((node)->references == 0)
+#define dns_rbtnode_refcurrent(node) ((node)->references)
+
+#if (__STDC_VERSION__ + 0) >= 199901L || defined __GNUC__
+static inline void
+dns_rbtnode_refincrement0(dns_rbtnode_t *node, unsigned int *refs) {
+ node->references++;
+ if (refs != NULL)
+ *refs = node->references;
+}
+
+static inline void
+dns_rbtnode_refincrement(dns_rbtnode_t *node, unsigned int *refs) {
+ ISC_REQUIRE(node->references > 0);
+ node->references++;
+ if (refs != NULL)
+ *refs = node->references;
+}
+
+static inline void
+dns_rbtnode_refdecrement(dns_rbtnode_t *node, unsigned int *refs) {
+ ISC_REQUIRE(node->references > 0);
+ node->references--;
+ if (refs != NULL)
+ *refs = node->references;
+}
+#else
+#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 { \
+ ISC_REQUIRE((node)->references > 0); \
+ (node)->references++; \
+ if ((refs) != NULL) \
+ (*refs) = (node)->references; \
+ } while (0)
+#define dns_rbtnode_refdecrement(node, refs) \
+ do { \
+ ISC_REQUIRE((node)->references > 0); \
+ (node)->references--; \
+ if ((refs) != NULL) \
+ (*refs) = (node)->references; \
+ } while (0)
+#endif
+#endif /* DNS_RBT_USEISCREFCOUNT */
+
+void
+dns_rbtnode_nodename(dns_rbtnode_t *node, dns_name_t *name);
+
+dns_rbtnode_t *
+dns_rbt_root(dns_rbt_t *rbt);
+
+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..59146af
--- /dev/null
+++ b/lib/dns/include/dns/rcode.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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..25220ce
--- /dev/null
+++ b/lib/dns/include/dns/rdata.h
@@ -0,0 +1,774 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#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* knowledge 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 <stdbool.h>
+
+#include <isc/lang.h>
+
+#include <dns/types.h>
+#include <dns/name.h>
+#include <dns/message.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_CHECKINITIALIZED
+#ifdef DNS_RDATA_CHECKINITIALIZED
+#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) true
+#endif
+#endif
+
+#define DNS_RDATA_UPDATE 0x0001 /*%< update pseudo record. */
+#define DNS_RDATA_OFFLINE 0x0002 /*%< RRSIG has a offline key. */
+
+#define DNS_RDATA_VALIDFLAGS(rdata) \
+ (((rdata)->flags & ~(DNS_RDATA_UPDATE|DNS_RDATA_OFFLINE)) == 0)
+
+/*
+ * The maximum length of a RDATA that can be sent on the wire.
+ * Max packet size (65535) less header (12), less name (1), type (2),
+ * class (2), ttl(4), length (2).
+ *
+ * None of the defined types that support name compression can exceed
+ * this and all new types are to be sent uncompressed.
+ */
+
+#define DNS_RDATA_MAXLENGTH 65512U
+
+/*
+ * 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 0x00000001ULL
+
+/*% Output explanatory comments. */
+#define DNS_STYLEFLAG_COMMENT 0x00000002ULL
+#define DNS_STYLEFLAG_RRCOMMENT 0x00000004ULL
+
+/*% Output KEYDATA in human readable format. */
+#define DNS_STYLEFLAG_KEYDATA 0x00000008ULL
+
+/*% Output textual RR type and RDATA in RFC 3597 unknown format */
+#define DNS_STYLEFLAG_UNKNOWNFORMAT 0x00000010ULL
+
+#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
+#define DNS_RDATA_UNKNOWNESCAPE 0x80000000
+
+/***
+ *** 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'
+ */
+
+int
+dns_rdata_casecompare(const dns_rdata_t *rdata1, const dns_rdata_t *rdata2);
+/*%<
+ * dns_rdata_casecompare() is similar to dns_rdata_compare() but also
+ * compares domain names case insensitively in known rdata types that
+ * are treated as opaque data by dns_rdata_compare().
+ *
+ * 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, unsigned int split_width,
+ const 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.
+ *
+ * base64 rdata text (e.g., DNSKEY records) will be split into chunks
+ * of 'split_width' characters. If split_width == 0, the text will
+ * not be split at all. If split_width == UINT_MAX (0xffffffff), then
+ * it is undefined and falls back to the default value of 'width'
+ */
+
+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(const 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().
+ */
+
+bool
+dns_rdatatype_ismeta(dns_rdatatype_t type);
+/*%<
+ * Return true iff the rdata type 'type' is a meta-type
+ * like ANY or AXFR.
+ */
+
+bool
+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.
+ *
+ */
+
+bool
+dns_rdataclass_ismeta(dns_rdataclass_t rdclass);
+/*%<
+ * Return true iff the rdata class 'rdclass' is a meta-class
+ * like ANY or NONE.
+ */
+
+bool
+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.
+ */
+
+bool
+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.
+ *
+ */
+
+bool
+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.
+ */
+
+bool
+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.
+ *
+ */
+
+bool
+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.
+ *
+ */
+
+bool
+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.
+ */
+
+bool
+dns_rdata_checkowner(dns_name_t* name, dns_rdataclass_t rdclass,
+ dns_rdatatype_t type, bool 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.
+ */
+
+bool
+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.
+ */
+
+void
+dns_rdata_exists(dns_rdata_t *rdata, dns_rdatatype_t type);
+
+void
+dns_rdata_notexist(dns_rdata_t *rdata, dns_rdatatype_t type);
+
+void
+dns_rdata_deleterrset(dns_rdata_t *rdata, dns_rdatatype_t type);
+
+void
+dns_rdata_makedelete(dns_rdata_t *rdata);
+
+const char *
+dns_rdata_updateop(dns_rdata_t *rdata, dns_section_t section);
+
+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..67fe1c7
--- /dev/null
+++ b/lib/dns/include/dns/rdataclass.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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
+ */
+
+isc_result_t
+dns_rdataclass_tounknowntext(dns_rdataclass_t rdclass, isc_buffer_t *target);
+/*%<
+ * Put textual RFC3597 CLASSXXXX 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..63015e1
--- /dev/null
+++ b/lib/dns/include/dns/rdatalist.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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;
+ /*%<
+ * Case vector. If the bit is set then the corresponding
+ * character in the owner name needs to be AND'd with 0x20,
+ * rendering that character upper case.
+ */
+ unsigned char upper[32];
+};
+
+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..5295d8e
--- /dev/null
+++ b/lib/dns/include/dns/rdataset.h
@@ -0,0 +1,710 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#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 <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/lang.h>
+#include <isc/magic.h>
+#include <isc/stdtime.h>
+
+#include <dns/types.h>
+#include <dns/rdatastruct.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);
+ void (*settrust)(dns_rdataset_t *rdataset,
+ dns_trust_t trust);
+ void (*expire)(dns_rdataset_t *rdataset);
+ void (*clearprefetch)(dns_rdataset_t *rdataset);
+ void (*setownercase)(dns_rdataset_t *rdataset,
+ const dns_name_t *name);
+ void (*getownercase)(const dns_rdataset_t *rdataset, dns_name_t *name);
+} 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 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.
+ */
+ 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_REQUIRED 0x00010000
+#define DNS_RDATASETATTR_REQUIREDGLUE DNS_RDATASETATTR_REQUIRED
+#define DNS_RDATASETATTR_LOADORDER 0x00020000
+#define DNS_RDATASETATTR_RESIGN 0x00040000
+#define DNS_RDATASETATTR_CLOSEST 0x00080000
+#define DNS_RDATASETATTR_OPTOUT 0x00100000 /*%< OPTOUT proof */
+#define DNS_RDATASETATTR_NEGATIVE 0x00200000
+#define DNS_RDATASETATTR_PREFETCH 0x00400000
+
+/*%
+ * _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.
+ */
+
+bool
+dns_rdataset_isassociated(dns_rdataset_t *rdataset);
+/*%<
+ * Is 'rdataset' associated?
+ *
+ * Requires:
+ *\li 'rdataset' is a valid rdataset.
+ *
+ * Returns:
+ *\li #true 'rdataset' is associated.
+ *\li #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,
+ bool omit_final_dot,
+ bool 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 #false. If it is
+ * #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 with 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.'
+ */
+
+void
+dns_rdataset_settrust(dns_rdataset_t *rdataset, dns_trust_t trust);
+/*%<
+ * Set the trust of the 'rdataset' to trust in any in the backing database.
+ * The local trust level of 'rdataset' is also set.
+ */
+
+void
+dns_rdataset_expire(dns_rdataset_t *rdataset);
+/*%<
+ * Mark the rdataset to be expired in the backing database.
+ */
+
+void
+dns_rdataset_clearprefetch(dns_rdataset_t *rdataset);
+/*%<
+ * Clear the PREFETCH attribute for the given rdataset in the
+ * underlying database.
+ *
+ * In the cache database, this signals that the rdataset is not
+ * eligible to be prefetched when the TTL is close to expiring.
+ * It has no function in other databases.
+ */
+
+void
+dns_rdataset_setownercase(dns_rdataset_t *rdataset, const dns_name_t *name);
+/*%<
+ * Store the casing of 'name', the owner name of 'rdataset', into
+ * a bitfield so that the name can be capitalized the same when when
+ * the rdataset is used later. This sets the CASESET attribute.
+ */
+
+void
+dns_rdataset_getownercase(const dns_rdataset_t *rdataset, dns_name_t *name);
+/*%<
+ * If the CASESET attribute is set, retrieve the case bitfield that was
+ * previously stored by dns_rdataset_getownername(), and capitalize 'name'
+ * according to it. If CASESET is not set, do nothing.
+ */
+
+void
+dns_rdataset_trimttl(dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset,
+ dns_rdata_rrsig_t *rrsig, isc_stdtime_t now,
+ bool acceptexpired);
+/*%<
+ * Trim the ttl of 'rdataset' and 'sigrdataset' so that they will expire
+ * at or before 'rrsig->expiretime'. If 'acceptexpired' is true and the
+ * signature has expired or will expire in the next 120 seconds, limit
+ * the ttl to be no more than 120 seconds.
+ *
+ * The ttl is further limited by the original ttl as stored in 'rrsig'
+ * and the original ttl values of 'rdataset' and 'sigrdataset'.
+ *
+ * Requires:
+ * \li 'rdataset' is a valid rdataset.
+ * \li 'sigrdataset' is a valid rdataset.
+ * \li 'rrsig' is non NULL.
+ */
+
+const char *
+dns_trust_totext(dns_trust_t trust);
+/*%<
+ * Display trust in textual form.
+ */
+
+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..54b3e90
--- /dev/null
+++ b/lib/dns/include/dns/rdatasetiter.h
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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..2e0d0e1
--- /dev/null
+++ b/lib/dns/include/dns/rdataslab.h
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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 <stdbool.h>
+
+#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.
+ */
+
+unsigned int
+dns_rdataslab_count(unsigned char *slab, unsigned int reservelen);
+/*%<
+ * Return the number of records in the rdataslab
+ *
+ * Requires:
+ *\li 'slab' points to a slab.
+ *
+ * Returns:
+ *\li The number of records in the slab.
+ */
+
+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
+ */
+
+bool
+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 true if the slabs are equal, false otherwise.
+ */
+bool
+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 true if the slabs are equal, #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..0e79232
--- /dev/null
+++ b/lib/dns/include/dns/rdatatype.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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
+ */
+
+isc_result_t
+dns_rdatatype_tounknowntext(dns_rdatatype_t type, isc_buffer_t *target);
+/*%<
+ * Put textual RFC3597 TYPEXXXX 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..672d3e9
--- /dev/null
+++ b/lib/dns/include/dns/request.h
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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 <stdbool.h>
+#include <isc/lang.h>
+#include <isc/event.h>
+
+#include <dns/types.h>
+
+#define DNS_REQUESTOPT_TCP 0x00000001U
+#define DNS_REQUESTOPT_CASE 0x00000002U
+#define DNS_REQUESTOPT_FIXEDID 0x00000004U
+#define DNS_REQUESTOPT_SHARE 0x00000008U
+
+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,
+ * #DNS_REQUESTOPT_SHARE option is set too, connecting TCP
+ * (vs. connected) will be shared too. The request
+ * will timeout after 'timeout' seconds.
+ *
+ *\li If the #DNS_REQUESTOPT_CASE option is set, use case sensitive
+ * compression.
+ *
+ *\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_createvia4() */
+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_createvia4() */
+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);
+
+/*% See dns_request_createvia4() */
+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);
+
+isc_result_t
+dns_request_createvia4(dns_requestmgr_t *requestmgr, dns_message_t *message,
+ isc_sockaddr_t *srcaddr, isc_sockaddr_t *destaddr,
+ isc_dscp_t dscp, 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,
+ * #DNS_REQUESTOPT_SHARE option is set too, connecting TCP
+ * (vs. connected) will be shared too. The request
+ * will timeout after 'timeout' seconds. UDP requests will be resent
+ * at 'udptimeout' intervals if non-zero or 'udpretries' is non-zero.
+ *
+ *\li If the #DNS_REQUESTOPT_CASE option is set, use case sensitive
+ * compression.
+ *
+ *\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_createraw4() */
+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_createraw4() */
+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);
+
+/*% See dns_request_createraw4() */
+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);
+
+isc_result_t
+dns_request_createraw4(dns_requestmgr_t *requestmgr, isc_buffer_t *msgbuf,
+ isc_sockaddr_t *srcaddr, isc_sockaddr_t *destaddr,
+ isc_dscp_t dscp, 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,
+ * #DNS_REQUESTOPT_SHARE option is set too, connecting TCP
+ * (vs. connected) will be shared too. 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.
+ */
+
+bool
+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
+ * #true, otherwise the result is based on the query message size.
+ *
+ * Requires:
+ *\li 'request' is a valid request.
+ *
+ * Returns:
+ *\li true if TCP was used.
+ *\li 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..c09107f
--- /dev/null
+++ b/lib/dns/include/dns/resolver.h
@@ -0,0 +1,714 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#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 <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/lang.h>
+#include <isc/socket.h>
+#include <isc/stats.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;
+ isc_result_t vresult;
+} dns_fetchevent_t;
+
+/*%
+ * The two quota types (fetches-per-zone and fetches-per-server)
+ */
+typedef enum {
+ dns_quotatype_zone = 0,
+ dns_quotatype_server
+} dns_quotatype_t;
+
+/*
+ * Options that modify how a 'fetch' is done.
+ */
+#define DNS_FETCHOPT_TCP 0x0001 /*%< Use TCP. */
+#define DNS_FETCHOPT_UNSHARED 0x0002 /*%< See below. */
+#define DNS_FETCHOPT_RECURSIVE 0x0004 /*%< Set RD? */
+#define DNS_FETCHOPT_NOEDNS0 0x0008 /*%< Do not use EDNS. */
+#define DNS_FETCHOPT_FORWARDONLY 0x0010 /*%< Only use forwarders. */
+#define DNS_FETCHOPT_NOVALIDATE 0x0020 /*%< Disable validation. */
+#define DNS_FETCHOPT_EDNS512 0x0040 /*%< Advertise a 512 byte
+ UDP buffer. */
+#define DNS_FETCHOPT_WANTNSID 0x0080 /*%< Request NSID */
+#define DNS_FETCHOPT_PREFETCH 0x0100 /*%< Do prefetch */
+#define DNS_FETCHOPT_NOCDFLAG 0x0200 /*%< Don't set CD flag. */
+#define DNS_FETCHOPT_NONTA 0x0400 /*%< Ignore NTA table. */
+/* RESERVED ECS 0x0000 */
+/* RESERVED ECS 0x1000 */
+/* RESERVED ECS 0x2000 */
+/* RESERVED TCPCLIENT 0x4000 */
+#define DNS_FETCHOPT_NOCACHED 0x8000 /*%< Force cache update. */
+
+/* Reserved in use by adb.c 0x00400000 */
+#define DNS_FETCHOPT_EDNSVERSIONSET 0x00800000
+#define DNS_FETCHOPT_EDNSVERSIONMASK 0xff000000
+#define DNS_FETCHOPT_EDNSVERSIONSHIFT 24
+
+/*
+ * Upper bounds of class of query RTT (ms). Corresponds to
+ * dns_resstatscounter_queryrttX statistics counters.
+ */
+#define DNS_RESOLVER_QRYRTTCLASS0 10
+#define DNS_RESOLVER_QRYRTTCLASS0STR "10"
+#define DNS_RESOLVER_QRYRTTCLASS1 100
+#define DNS_RESOLVER_QRYRTTCLASS1STR "100"
+#define DNS_RESOLVER_QRYRTTCLASS2 500
+#define DNS_RESOLVER_QRYRTTCLASS2STR "500"
+#define DNS_RESOLVER_QRYRTTCLASS3 800
+#define DNS_RESOLVER_QRYRTTCLASS3STR "800"
+#define DNS_RESOLVER_QRYRTTCLASS4 1600
+#define DNS_RESOLVER_QRYRTTCLASS4STR "1600"
+
+/*
+ * 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, unsigned int ndisp,
+ 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 dispatch with an IPv4 UDP socket, or is NULL.
+ * If not NULL, 'ndisp' clones of it will be created by the resolver.
+ *
+ *\li 'dispatchv6' is a dispatch with an IPv6 UDP socket, or is NULL.
+ * If not NULL, 'ndisp' clones of it will be created by the resolver.
+ *
+ *\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 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, 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);
+isc_result_t
+dns_resolver_createfetch3(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, uint16_t id,
+ unsigned int options, unsigned int depth,
+ isc_counter_t *qc, 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.
+ */
+
+void
+dns_resolver_logfetch(dns_fetch_t *fetch, isc_log_t *lctx,
+ isc_logcategory_t *category, isc_logmodule_t *module,
+ int level, bool duplicateok);
+/*%<
+ * Dump a log message on internal state at the completion of given 'fetch'.
+ * 'lctx', 'category', 'module', and 'level' are used to write the log message.
+ * By default, only one log message is written even if the corresponding fetch
+ * context serves multiple clients; if 'duplicateok' is true the suppression
+ * is disabled and the message can be written every time this function is
+ * called.
+ *
+ * Requires:
+ *
+ *\li 'fetch' is a valid fetch, and has completed.
+ */
+
+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);
+
+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, 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, uint16_t udpsize);
+/*%<
+ * Set the EDNS UDP buffer size advertised by the server.
+ */
+
+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.
+ */
+
+void
+dns_resolver_reset_ds_digests(dns_resolver_t *resolver);
+/*%<
+ * Clear the disabled DS/DLV digest types.
+ */
+
+isc_result_t
+dns_resolver_disable_algorithm(dns_resolver_t *resolver, dns_name_t *name,
+ unsigned int alg);
+/*%<
+ * Mark the given 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_result_t
+dns_resolver_disable_ds_digest(dns_resolver_t *resolver, dns_name_t *name,
+ unsigned int digest_type);
+/*%<
+ * Mark the given DS/DLV digest type as disabled and below 'name'.
+ * Valid types are less than 256.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_RANGE
+ *\li #ISC_R_NOMEMORY
+ */
+
+bool
+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 whether the algorithm has been disabled via
+ * dns_resolver_disable_algorithm(), then checks the underlying
+ * crypto libraries if it was not specifically disabled.
+ */
+
+bool
+dns_resolver_ds_digest_supported(dns_resolver_t *resolver, dns_name_t *name,
+ unsigned int digest_type);
+/*%<
+ * Check if the given digest type is supported by this resolver.
+ * This checks whether the digest type has been disabled via
+ * dns_resolver_disable_ds_digest(), then checks the underlying
+ * crypto libraries if it was not specifically disabled.
+ */
+
+void
+dns_resolver_resetmustbesecure(dns_resolver_t *resolver);
+
+isc_result_t
+dns_resolver_setmustbesecure(dns_resolver_t *resolver, dns_name_t *name,
+ bool value);
+
+bool
+dns_resolver_getmustbesecure(dns_resolver_t *resolver, dns_name_t *name);
+
+
+void
+dns_resolver_settimeout(dns_resolver_t *resolver, unsigned int seconds);
+/*%<
+ * Set the length of time the resolver will work on a query, in seconds.
+ *
+ * If timeout is 0, the default timeout will be applied.
+ *
+ * Requires:
+ * \li resolver to be valid.
+ */
+
+unsigned int
+dns_resolver_gettimeout(dns_resolver_t *resolver);
+/*%<
+ * Get the current length of time the resolver will work on a query, in seconds.
+ *
+ * Requires:
+ * \li resolver to be valid.
+ */
+
+void
+dns_resolver_setclientsperquery(dns_resolver_t *resolver,
+ uint32_t min, uint32_t max);
+void
+dns_resolver_setfetchesperzone(dns_resolver_t *resolver, uint32_t clients);
+
+void
+dns_resolver_getclientsperquery(dns_resolver_t *resolver, uint32_t *cur,
+ uint32_t *min, uint32_t *max);
+
+bool
+dns_resolver_getzeronosoattl(dns_resolver_t *resolver);
+
+void
+dns_resolver_setzeronosoattl(dns_resolver_t *resolver, bool state);
+
+unsigned int
+dns_resolver_getoptions(dns_resolver_t *resolver);
+
+void
+dns_resolver_addbadcache(dns_resolver_t *resolver, dns_name_t *name,
+ dns_rdatatype_t type, isc_time_t *expire);
+/*%<
+ * Add a entry to the bad cache for <name,type> that will expire at 'expire'.
+ *
+ * Requires:
+ * \li resolver to be valid.
+ * \li name to be valid.
+ */
+
+bool
+dns_resolver_getbadcache(dns_resolver_t *resolver, dns_name_t *name,
+ dns_rdatatype_t type, isc_time_t *now);
+/*%<
+ * Check to see if there is a unexpired entry in the bad cache for
+ * <name,type>.
+ *
+ * Requires:
+ * \li resolver to be valid.
+ * \li name to be valid.
+ */
+
+void
+dns_resolver_flushbadcache(dns_resolver_t *resolver, dns_name_t *name);
+/*%<
+ * Flush the bad cache of all entries at 'name' if 'name' is non NULL.
+ * Flush the entire bad cache if 'name' is NULL.
+ *
+ * Requires:
+ * \li resolver to be valid.
+ */
+
+void
+dns_resolver_flushbadnames(dns_resolver_t *resolver, dns_name_t *name);
+/*%<
+ * Flush the bad cache of all entries at or below 'name'.
+ *
+ * Requires:
+ * \li resolver to be valid.
+ * \li name != NULL
+ */
+
+void
+dns_resolver_printbadcache(dns_resolver_t *resolver, FILE *fp);
+/*%
+ * Print out the contents of the bad cache to 'fp'.
+ *
+ * Requires:
+ * \li resolver to be valid.
+ */
+
+void
+dns_resolver_setquerydscp4(dns_resolver_t *resolver, isc_dscp_t dscp);
+isc_dscp_t
+dns_resolver_getquerydscp4(dns_resolver_t *resolver);
+
+void
+dns_resolver_setquerydscp6(dns_resolver_t *resolver, isc_dscp_t dscp);
+isc_dscp_t
+dns_resolver_getquerydscp6(dns_resolver_t *resolver);
+/*%
+ * Get and set the DSCP values for the resolver's IPv4 and IPV6 query
+ * sources.
+ *
+ * Requires:
+ * \li resolver to be valid.
+ */
+
+void
+dns_resolver_setmaxdepth(dns_resolver_t *resolver, unsigned int maxdepth);
+unsigned int
+dns_resolver_getmaxdepth(dns_resolver_t *resolver);
+/*%
+ * Get and set how many NS indirections will be followed when looking for
+ * nameserver addresses.
+ *
+ * Requires:
+ * \li resolver to be valid.
+ */
+
+void
+dns_resolver_setmaxqueries(dns_resolver_t *resolver, unsigned int queries);
+unsigned int
+dns_resolver_getmaxqueries(dns_resolver_t *resolver);
+/*%
+ * Get and set how many iterative queries will be allowed before
+ * terminating a recursive query.
+ *
+ * Requires:
+ * \li resolver to be valid.
+ */
+
+void
+dns_resolver_setquotaresponse(dns_resolver_t *resolver,
+ dns_quotatype_t which, isc_result_t resp);
+isc_result_t
+dns_resolver_getquotaresponse(dns_resolver_t *resolver, dns_quotatype_t which);
+/*%
+ * Get and set the result code that will be used when quotas
+ * are exceeded. If 'which' is set to quotatype "zone", then the
+ * result specified in 'resp' will be used when the fetches-per-zone
+ * quota is exceeded by a fetch. If 'which' is set to quotatype "server",
+ * then the reuslt specified in 'resp' will be used when the
+ * fetches-per-server quota has been exceeded for all the
+ * authoritative servers for a zone. Valid choices are
+ * DNS_R_DROP or DNS_R_SERVFAIL.
+ *
+ * Requires:
+ * \li 'resolver' to be valid.
+ * \li 'which' to be dns_quotatype_zone or dns_quotatype_server
+ * \li 'resp' to be DNS_R_DROP or DNS_R_SERVFAIL.
+ */
+
+void
+dns_resolver_dumpfetches(dns_resolver_t *resolver,
+ isc_statsformat_t format, FILE *fp);
+
+
+#ifdef ENABLE_AFL
+/*%
+ * Enable fuzzing of resolver, changes behaviour and eliminates retries
+ */
+void dns_resolver_setfuzzing(void);
+#endif
+
+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..92fa5e1
--- /dev/null
+++ b/lib/dns/include/dns/result.h
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#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_NOTMASTER (ISC_RESULTCLASS_DNS + 105)
+#define DNS_R_BROKENCHAIN (ISC_RESULTCLASS_DNS + 106)
+#define DNS_R_EXPIRED (ISC_RESULTCLASS_DNS + 107)
+#define DNS_R_NOTDYNAMIC (ISC_RESULTCLASS_DNS + 108)
+#define DNS_R_BADEUI (ISC_RESULTCLASS_DNS + 109)
+#define DNS_R_NTACOVERED (ISC_RESULTCLASS_DNS + 110)
+#define DNS_R_BADCDS (ISC_RESULTCLASS_DNS + 111)
+#define DNS_R_BADCDNSKEY (ISC_RESULTCLASS_DNS + 112)
+#define DNS_R_OPTERR (ISC_RESULTCLASS_DNS + 113)
+#define DNS_R_BADDNSTAP (ISC_RESULTCLASS_DNS + 114)
+#define DNS_R_BADTSIG (ISC_RESULTCLASS_DNS + 115)
+#define DNS_R_BADSIG0 (ISC_RESULTCLASS_DNS + 116)
+#define DNS_R_TOOMANYRECORDS (ISC_RESULTCLASS_DNS + 117)
+
+#define DNS_R_NRESULTS 118 /*%< 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_RCODE11 (ISC_RESULTCLASS_DNSRCODE + 11)
+#define DNS_R_RCODE12 (ISC_RESULTCLASS_DNSRCODE + 12)
+#define DNS_R_RCODE13 (ISC_RESULTCLASS_DNSRCODE + 13)
+#define DNS_R_RCODE14 (ISC_RESULTCLASS_DNSRCODE + 14)
+#define DNS_R_RCODE15 (ISC_RESULTCLASS_DNSRCODE + 15)
+#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..f6a1819
--- /dev/null
+++ b/lib/dns/include/dns/rootns.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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/rpz.h b/lib/dns/include/dns/rpz.h
new file mode 100644
index 0000000..78f3443
--- /dev/null
+++ b/lib/dns/include/dns/rpz.h
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $Id$ */
+
+
+#ifndef DNS_RPZ_H
+#define DNS_RPZ_H 1
+
+#include <stdbool.h>
+
+#include <isc/deprecated.h>
+#include <isc/event.h>
+#include <isc/ht.h>
+#include <isc/lang.h>
+#include <isc/refcount.h>
+#include <isc/rwlock.h>
+
+#include <dns/fixedname.h>
+#include <dns/rdata.h>
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+#define DNS_RPZ_PREFIX "rpz-"
+/*
+ * Sub-zones of various trigger types.
+ */
+#define DNS_RPZ_CLIENT_IP_ZONE DNS_RPZ_PREFIX"client-ip"
+#define DNS_RPZ_IP_ZONE DNS_RPZ_PREFIX"ip"
+#define DNS_RPZ_NSIP_ZONE DNS_RPZ_PREFIX"nsip"
+#define DNS_RPZ_NSDNAME_ZONE DNS_RPZ_PREFIX"nsdname"
+/*
+ * Special policies.
+ */
+#define DNS_RPZ_PASSTHRU_NAME DNS_RPZ_PREFIX"passthru"
+#define DNS_RPZ_DROP_NAME DNS_RPZ_PREFIX"drop"
+#define DNS_RPZ_TCP_ONLY_NAME DNS_RPZ_PREFIX"tcp-only"
+
+
+typedef uint8_t dns_rpz_prefix_t;
+
+typedef enum {
+ DNS_RPZ_TYPE_BAD,
+ DNS_RPZ_TYPE_CLIENT_IP,
+ DNS_RPZ_TYPE_QNAME,
+ DNS_RPZ_TYPE_IP,
+ DNS_RPZ_TYPE_NSDNAME,
+ DNS_RPZ_TYPE_NSIP
+} dns_rpz_type_t;
+
+/*
+ * Require DNS_RPZ_POLICY_PASSTHRU < DNS_RPZ_POLICY_DROP
+ * < DNS_RPZ_POLICY_TCP_ONLY DNS_RPZ_POLICY_NXDOMAIN < DNS_RPZ_POLICY_NODATA
+ * < DNS_RPZ_POLICY_CNAME to choose among competing policies.
+ */
+typedef enum {
+ DNS_RPZ_POLICY_GIVEN = 0, /* 'given': what policy record says */
+ DNS_RPZ_POLICY_DISABLED = 1, /* log what would have happened */
+ DNS_RPZ_POLICY_PASSTHRU = 2, /* 'passthru': do not rewrite */
+ DNS_RPZ_POLICY_DROP = 3, /* 'drop': do not respond */
+ DNS_RPZ_POLICY_TCP_ONLY = 4, /* 'tcp-only': answer UDP with TC=1 */
+ DNS_RPZ_POLICY_NXDOMAIN = 5, /* 'nxdomain': answer with NXDOMAIN */
+ DNS_RPZ_POLICY_NODATA = 6, /* 'nodata': answer with ANCOUNT=0 */
+ DNS_RPZ_POLICY_CNAME = 7, /* 'cname x': answer with x's rrsets */
+ DNS_RPZ_POLICY_RECORD,
+ DNS_RPZ_POLICY_WILDCNAME,
+ DNS_RPZ_POLICY_MISS,
+ DNS_RPZ_POLICY_ERROR
+} dns_rpz_policy_t;
+
+typedef uint8_t dns_rpz_num_t;
+
+#define DNS_RPZ_MAX_ZONES 32
+#if DNS_RPZ_MAX_ZONES > 32
+# if DNS_RPZ_MAX_ZONES > 64
+# error "rpz zone bit masks must fit in a word"
+# endif
+typedef uint64_t dns_rpz_zbits_t;
+#else
+typedef uint32_t dns_rpz_zbits_t;
+#endif
+
+#define DNS_RPZ_ALL_ZBITS ((dns_rpz_zbits_t)-1)
+
+#define DNS_RPZ_INVALID_NUM DNS_RPZ_MAX_ZONES
+
+#define DNS_RPZ_ZBIT(n) (((dns_rpz_zbits_t)1) << (dns_rpz_num_t)(n))
+
+/*
+ * Mask of the specified and higher numbered policy zones
+ * Avoid hassles with (1<<33) or (1<<65)
+ */
+#define DNS_RPZ_ZMASK(n) ((dns_rpz_zbits_t)((((n) >= DNS_RPZ_MAX_ZONES-1) ? \
+ 0 : (1<<((n)+1))) -1))
+
+/*
+ * The trigger counter type.
+ */
+typedef size_t dns_rpz_trigger_counter_t;
+
+/*
+ * The number of triggers of each type in a response policy zone.
+ */
+typedef struct dns_rpz_triggers dns_rpz_triggers_t;
+struct dns_rpz_triggers {
+ dns_rpz_trigger_counter_t client_ipv4;
+ dns_rpz_trigger_counter_t client_ipv6;
+ dns_rpz_trigger_counter_t qname;
+ dns_rpz_trigger_counter_t ipv4;
+ dns_rpz_trigger_counter_t ipv6;
+ dns_rpz_trigger_counter_t nsdname;
+ dns_rpz_trigger_counter_t nsipv4;
+ dns_rpz_trigger_counter_t nsipv6;
+};
+
+/*
+ * A single response policy zone.
+ */
+typedef struct dns_rpz_zone dns_rpz_zone_t;
+struct dns_rpz_zone {
+ isc_refcount_t refs;
+ dns_rpz_num_t num; /* ordinal in list of policy zones */
+ dns_name_t origin; /* Policy zone name */
+ dns_name_t client_ip; /* DNS_RPZ_CLIENT_IP_ZONE.origin. */
+ dns_name_t ip; /* DNS_RPZ_IP_ZONE.origin. */
+ dns_name_t nsdname; /* DNS_RPZ_NSDNAME_ZONE.origin */
+ dns_name_t nsip; /* DNS_RPZ_NSIP_ZONE.origin. */
+ dns_name_t passthru; /* DNS_RPZ_PASSTHRU_NAME. */
+ dns_name_t drop; /* DNS_RPZ_DROP_NAME. */
+ dns_name_t tcp_only; /* DNS_RPZ_TCP_ONLY_NAME. */
+ dns_name_t cname; /* override value for ..._CNAME */
+ dns_ttl_t max_policy_ttl;
+ dns_rpz_policy_t policy; /* DNS_RPZ_POLICY_GIVEN or override */
+};
+
+/*
+ * Radix tree node for response policy IP addresses
+ */
+typedef struct dns_rpz_cidr_node dns_rpz_cidr_node_t;
+
+/*
+ * Bitfields indicating which policy zones have policies of
+ * which type.
+ */
+typedef struct dns_rpz_have dns_rpz_have_t;
+struct dns_rpz_have {
+ dns_rpz_zbits_t client_ipv4;
+ dns_rpz_zbits_t client_ipv6;
+ dns_rpz_zbits_t client_ip;
+ dns_rpz_zbits_t qname;
+ dns_rpz_zbits_t ipv4;
+ dns_rpz_zbits_t ipv6;
+ dns_rpz_zbits_t ip;
+ dns_rpz_zbits_t nsdname;
+ dns_rpz_zbits_t nsipv4;
+ dns_rpz_zbits_t nsipv6;
+ dns_rpz_zbits_t nsip;
+ dns_rpz_zbits_t qname_skip_recurse;
+};
+
+/*
+ * Policy options
+ */
+typedef struct dns_rpz_popt dns_rpz_popt_t;
+struct dns_rpz_popt {
+ dns_rpz_zbits_t no_rd_ok;
+ dns_rpz_zbits_t no_log;
+ bool break_dnssec;
+ bool qname_wait_recurse;
+ bool nsip_wait_recurse;
+ unsigned int min_ns_labels;
+ dns_rpz_num_t num_zones;
+};
+
+/*
+ * Response policy zones known to a view.
+ */
+typedef struct dns_rpz_zones dns_rpz_zones_t;
+struct dns_rpz_zones {
+ dns_rpz_popt_t p;
+ dns_rpz_zone_t *zones[DNS_RPZ_MAX_ZONES];
+ dns_rpz_triggers_t triggers[DNS_RPZ_MAX_ZONES];
+
+ /*
+ * RPZ policy version number (initially 0, increases whenever
+ * the server is reconfigured with new zones or policy)
+ */
+ int rpz_ver;
+
+ dns_rpz_zbits_t defined;
+
+ /*
+ * The set of records for a policy zone are in one of these states:
+ * never loaded load_begun=0 have=0
+ * during initial loading load_begun=1 have=0
+ * and rbtdb->rpzsp == rbtdb->load_rpzsp
+ * after good load load_begun=1 have!=0
+ * after failed initial load load_begun=1 have=0
+ * and rbtdb->load_rpzsp == NULL
+ * reloading after failure load_begun=1 have=0
+ * reloading after success
+ * main rpzs load_begun=1 have!=0
+ * load rpzs load_begun=1 have=0
+ */
+ dns_rpz_zbits_t load_begun;
+ dns_rpz_have_t have;
+
+ /*
+ * total_triggers maintains the total number of triggers in all
+ * policy zones in the view. It is only used to print summary
+ * statistics after a zone load of how the trigger counts
+ * changed.
+ */
+ dns_rpz_triggers_t total_triggers;
+
+ isc_mem_t *mctx;
+ isc_refcount_t refs;
+ /*
+ * One lock for short term read-only search that guarantees the
+ * consistency of the pointers.
+ * A second lock for maintenance that guarantees no other thread
+ * is adding or deleting nodes.
+ */
+ isc_rwlock_t search_lock;
+ isc_mutex_t maint_lock;
+
+ dns_rpz_cidr_node_t *cidr;
+ dns_rbt_t *rbt;
+};
+
+
+/*
+ * context for finding the best policy
+ */
+typedef struct {
+ unsigned int state;
+# define DNS_RPZ_REWRITTEN 0x0001
+# define DNS_RPZ_DONE_CLIENT_IP 0x0002 /* client IP address checked */
+# define DNS_RPZ_DONE_QNAME 0x0004 /* qname checked */
+# define DNS_RPZ_DONE_QNAME_IP 0x0008 /* IP addresses of qname checked */
+# define DNS_RPZ_DONE_NSDNAME 0x0010 /* NS name missed; checking addresses */
+# define DNS_RPZ_DONE_IPv4 0x0020
+# define DNS_RPZ_RECURSING 0x0040
+# define DNS_RPZ_ACTIVE 0x0080
+ /*
+ * Best match so far.
+ */
+ struct {
+ dns_rpz_type_t type;
+ dns_rpz_zone_t *rpz;
+ dns_rpz_prefix_t prefix;
+ dns_rpz_policy_t policy;
+ dns_ttl_t ttl;
+ isc_result_t result;
+ dns_zone_t *zone;
+ dns_db_t *db;
+ dns_dbversion_t *version;
+ dns_dbnode_t *node;
+ dns_rdataset_t *rdataset;
+ } m;
+ /*
+ * State for chasing IP addresses and NS names including recursion.
+ */
+ struct {
+ unsigned int label;
+ dns_db_t *db;
+ dns_rdataset_t *ns_rdataset;
+ dns_rdatatype_t r_type;
+ isc_result_t r_result;
+ dns_rdataset_t *r_rdataset;
+ } r;
+
+ /*
+ * State of real query while recursing for NSIP or NSDNAME.
+ */
+ struct {
+ isc_result_t result;
+ bool is_zone;
+ bool authoritative;
+ dns_zone_t *zone;
+ dns_db_t *db;
+ dns_dbnode_t *node;
+ dns_rdataset_t *rdataset;
+ dns_rdataset_t *sigrdataset;
+ dns_rdatatype_t qtype;
+ } q;
+
+ /*
+ * A copy of the 'have' and 'p' structures and the RPZ
+ * policy version as of the beginning of RPZ processing,
+ * used to avoid problems when policy is updated while
+ * RPZ recursion is ongoing.
+ */
+ dns_rpz_have_t have;
+ dns_rpz_popt_t popt;
+ int rpz_ver;
+
+ /*
+ * p_name: current policy owner name
+ * r_name: recursing for this name to possible policy triggers
+ * f_name: saved found name from before recursion
+ */
+ dns_name_t *p_name;
+ dns_name_t *r_name;
+ dns_name_t *fname;
+ dns_fixedname_t _p_namef;
+ dns_fixedname_t _r_namef;
+ dns_fixedname_t _fnamef;
+} dns_rpz_st_t;
+
+#define DNS_RPZ_TTL_DEFAULT 5
+#define DNS_RPZ_MAX_TTL_DEFAULT DNS_RPZ_TTL_DEFAULT
+
+/*
+ * So various response policy zone messages can be turned up or down.
+ */
+#define DNS_RPZ_ERROR_LEVEL ISC_LOG_WARNING
+#define DNS_RPZ_INFO_LEVEL ISC_LOG_INFO
+#define DNS_RPZ_DEBUG_LEVEL1 ISC_LOG_DEBUG(1)
+#define DNS_RPZ_DEBUG_LEVEL2 ISC_LOG_DEBUG(2)
+#define DNS_RPZ_DEBUG_LEVEL3 ISC_LOG_DEBUG(3)
+#define DNS_RPZ_DEBUG_QUIET (DNS_RPZ_DEBUG_LEVEL3+1)
+
+const char *
+dns_rpz_type2str(dns_rpz_type_t type);
+
+dns_rpz_policy_t
+dns_rpz_str2policy(const char *str);
+
+const char *
+dns_rpz_policy2str(dns_rpz_policy_t policy);
+
+dns_rpz_policy_t
+dns_rpz_decode_cname(dns_rpz_zone_t *rpz, dns_rdataset_t *rdataset,
+ dns_name_t *selfname);
+
+isc_result_t
+dns_rpz_new_zones(dns_rpz_zones_t **rpzsp, isc_mem_t *mctx);
+
+void
+dns_rpz_attach_rpzs(dns_rpz_zones_t *source, dns_rpz_zones_t **target);
+
+void
+dns_rpz_detach_rpzs(dns_rpz_zones_t **rpzsp);
+
+isc_result_t
+dns_rpz_beginload(dns_rpz_zones_t **load_rpzsp,
+ dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num);
+
+isc_result_t
+dns_rpz_ready(dns_rpz_zones_t *rpzs,
+ dns_rpz_zones_t **load_rpzsp, dns_rpz_num_t rpz_num);
+
+isc_result_t
+dns_rpz_add(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num, dns_name_t *name);
+
+void
+dns_rpz_delete(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num, dns_name_t *name);
+
+dns_rpz_num_t
+dns_rpz_find_ip(dns_rpz_zones_t *rpzs, dns_rpz_type_t rpz_type,
+ dns_rpz_zbits_t zbits, const isc_netaddr_t *netaddr,
+ dns_name_t *ip_name, dns_rpz_prefix_t *prefixp);
+
+dns_rpz_zbits_t
+dns_rpz_find_name(dns_rpz_zones_t *rpzs, dns_rpz_type_t rpz_type,
+ dns_rpz_zbits_t zbits, dns_name_t *trig_name);
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_RPZ_H */
diff --git a/lib/dns/include/dns/rriterator.h b/lib/dns/include/dns/rriterator.h
new file mode 100644
index 0000000..8c0c076
--- /dev/null
+++ b/lib/dns/include/dns/rriterator.h
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $Id: rriterator.h,v 1.4 2011/11/01 23:47:00 tbox Exp $ */
+
+#ifndef DNS_RRITERATOR_H
+#define DNS_RRITERATOR_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file dns/rriterator.h
+ * \brief
+ * Functions for "walking" a zone database, visiting each RR or RRset in turn.
+ */
+
+/*****
+ ***** Imports
+ *****/
+
+#include <inttypes.h>
+
+#include <isc/lang.h>
+#include <isc/magic.h>
+#include <isc/ondestroy.h>
+#include <isc/stdtime.h>
+
+#include <dns/db.h>
+#include <dns/dbiterator.h>
+#include <dns/fixedname.h>
+#include <dns/name.h>
+#include <dns/rdata.h>
+#include <dns/rdataset.h>
+#include <dns/rdatasetiter.h>
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+/*****
+ ***** Types
+ *****/
+
+/*%
+ * A dns_rriterator_t is an iterator that iterates over an entire database,
+ * returning one RR at a time, in some arbitrary order.
+ */
+
+typedef struct dns_rriterator {
+ unsigned int magic;
+ isc_result_t result;
+ dns_db_t *db;
+ dns_dbiterator_t *dbit;
+ dns_dbversion_t *ver;
+ isc_stdtime_t now;
+ dns_dbnode_t *node;
+ dns_fixedname_t fixedname;
+ dns_rdatasetiter_t *rdatasetit;
+ dns_rdataset_t rdataset;
+ dns_rdata_t rdata;
+} dns_rriterator_t;
+
+#define RRITERATOR_MAGIC ISC_MAGIC('R', 'R', 'I', 't')
+#define VALID_RRITERATOR(m) ISC_MAGIC_VALID(m, RRITERATOR_MAGIC)
+
+isc_result_t
+dns_rriterator_init(dns_rriterator_t *it, dns_db_t *db,
+ dns_dbversion_t *ver, isc_stdtime_t now);
+/*%
+ * Initialize an rriterator; sets the cursor to the origin node
+ * of the database.
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database.
+ *
+ * Returns:
+ *
+ * \li #ISC_R_SUCCESS
+ * \li #ISC_R_NOMEMORY
+ */
+
+isc_result_t
+dns_rriterator_first(dns_rriterator_t *it);
+/*%<
+ * Move the rriterator cursor to the first rdata in the database.
+ *
+ * Requires:
+ *\li 'it' is a valid, initialized rriterator
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMORE There are no rdata in the set.
+ */
+
+isc_result_t
+dns_rriterator_nextrrset(dns_rriterator_t *it);
+/*%<
+ * Move the rriterator cursor to the next rrset in the database,
+ * skipping over any remaining records that have the same rdatatype
+ * as the current one.
+ *
+ * Requires:
+ *\li 'it' is a valid, initialized rriterator
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMORE No more rrsets in the database
+ */
+
+isc_result_t
+dns_rriterator_next(dns_rriterator_t *it);
+/*%<
+ * Move the rriterator cursor to the next rrset in the database,
+ * skipping over any remaining records that have the same rdatatype
+ * as the current one.
+ *
+ * Requires:
+ *\li 'it' is a valid, initialized rriterator
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMORE No more records in the database
+ */
+
+void
+dns_rriterator_current(dns_rriterator_t *it, dns_name_t **name,
+ uint32_t *ttl, dns_rdataset_t **rdataset,
+ dns_rdata_t **rdata);
+/*%<
+ * Make '*name' refer to the current name. If 'rdataset' is not NULL,
+ * make '*rdataset' refer to the current * rdataset. If '*rdata' is not
+ * NULL, make '*rdata' refer to the current record.
+ *
+ * Requires:
+ *\li '*name' is a valid name object
+ *\li 'rdataset' is NULL or '*rdataset' is NULL
+ *\li 'rdata' is NULL or '*rdata' is NULL
+ *
+ * Ensures:
+ *\li 'rdata' refers to the rdata at the rdata cursor location of
+ *\li 'rdataset'.
+ */
+
+void
+dns_rriterator_pause(dns_rriterator_t *it);
+/*%<
+ * Pause rriterator. Frees any locks held by the database iterator.
+ * Callers should use this routine any time they are not going to
+ * execute another rriterator method in the immediate future.
+ *
+ * Requires:
+ *\li 'it' is a valid iterator.
+ *
+ * Ensures:
+ *\li Any database locks being held for efficiency of iterator access are
+ * released.
+ */
+
+void
+dns_rriterator_destroy(dns_rriterator_t *it);
+/*%<
+ * Shut down and free resources in rriterator 'it'.
+ *
+ * Requires:
+ *
+ *\li 'it' is a valid iterator.
+ *
+ * Ensures:
+ *
+ *\li All resources used by the rriterator are freed.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_RRITERATOR_H */
diff --git a/lib/dns/include/dns/rrl.h b/lib/dns/include/dns/rrl.h
new file mode 100644
index 0000000..0301ed8
--- /dev/null
+++ b/lib/dns/include/dns/rrl.h
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#ifndef DNS_RRL_H
+#define DNS_RRL_H 1
+
+/*
+ * Rate limit DNS responses.
+ */
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/lang.h>
+
+#include <dns/fixedname.h>
+#include <dns/rdata.h>
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+
+/*
+ * Memory allocation or other failures.
+ */
+#define DNS_RRL_LOG_FAIL ISC_LOG_WARNING
+/*
+ * dropped or slipped responses.
+ */
+#define DNS_RRL_LOG_DROP ISC_LOG_INFO
+/*
+ * Major events in dropping or slipping.
+ */
+#define DNS_RRL_LOG_DEBUG1 ISC_LOG_DEBUG(3)
+/*
+ * Limit computations.
+ */
+#define DNS_RRL_LOG_DEBUG2 ISC_LOG_DEBUG(4)
+/*
+ * Even less interesting.
+ */
+#define DNS_RRL_LOG_DEBUG3 ISC_LOG_DEBUG(9)
+
+
+#define DNS_RRL_LOG_ERR_LEN 64
+#define DNS_RRL_LOG_BUF_LEN (sizeof("would continue limiting") + \
+ DNS_RRL_LOG_ERR_LEN + \
+ sizeof(" responses to ") + \
+ ISC_NETADDR_FORMATSIZE + \
+ sizeof("/128 for IN ") + \
+ DNS_RDATATYPE_FORMATSIZE + \
+ DNS_NAME_FORMATSIZE)
+
+
+typedef struct dns_rrl_hash dns_rrl_hash_t;
+
+/*
+ * Response types.
+ */
+typedef enum {
+ DNS_RRL_RTYPE_FREE = 0,
+ DNS_RRL_RTYPE_QUERY,
+ DNS_RRL_RTYPE_REFERRAL,
+ DNS_RRL_RTYPE_NODATA,
+ DNS_RRL_RTYPE_NXDOMAIN,
+ DNS_RRL_RTYPE_ERROR,
+ DNS_RRL_RTYPE_ALL,
+ DNS_RRL_RTYPE_TCP,
+} dns_rrl_rtype_t;
+
+/*
+ * A rate limit bucket key.
+ * This should be small to limit the total size of the database.
+ * The hash of the qname should be wide enough to make the probability
+ * of collisions among requests from a single IP address block less than 50%.
+ * We need a 32-bit hash value for 10000 qps (e.g. random qnames forged
+ * by attacker) to collide with legitimate qnames from the target with
+ * probability at most 1%.
+ */
+#define DNS_RRL_MAX_PREFIX 64
+typedef union dns_rrl_key dns_rrl_key_t;
+struct dns__rrl_key {
+ uint32_t ip[DNS_RRL_MAX_PREFIX/32];
+ uint32_t qname_hash;
+ dns_rdatatype_t qtype;
+ uint8_t qclass;
+ unsigned int rtype :4; /* dns_rrl_rtype_t */
+ unsigned int ipv6 :1;
+};
+union dns_rrl_key {
+ struct dns__rrl_key s;
+ uint16_t w[sizeof(struct dns__rrl_key)/sizeof(uint16_t)];
+};
+
+/*
+ * A rate-limit entry.
+ * This should be small to limit the total size of the table of entries.
+ */
+typedef struct dns_rrl_entry dns_rrl_entry_t;
+typedef ISC_LIST(dns_rrl_entry_t) dns_rrl_bin_t;
+struct dns_rrl_entry {
+ ISC_LINK(dns_rrl_entry_t) lru;
+ ISC_LINK(dns_rrl_entry_t) hlink;
+ dns_rrl_key_t key;
+# define DNS_RRL_RESPONSE_BITS 24
+ signed int responses :DNS_RRL_RESPONSE_BITS;
+# define DNS_RRL_QNAMES_BITS 8
+ unsigned int log_qname :DNS_RRL_QNAMES_BITS;
+
+# define DNS_RRL_TS_GEN_BITS 2
+ unsigned int ts_gen :DNS_RRL_TS_GEN_BITS;
+ unsigned int ts_valid :1;
+# define DNS_RRL_HASH_GEN_BITS 1
+ unsigned int hash_gen :DNS_RRL_HASH_GEN_BITS;
+ unsigned int logged :1;
+# define DNS_RRL_LOG_BITS 11
+ unsigned int log_secs :DNS_RRL_LOG_BITS;
+
+# define DNS_RRL_TS_BITS 12
+ unsigned int ts :DNS_RRL_TS_BITS;
+
+# define DNS_RRL_MAX_SLIP 10
+ unsigned int slip_cnt :4;
+};
+
+#define DNS_RRL_MAX_TIME_TRAVEL 5
+#define DNS_RRL_FOREVER (1<<DNS_RRL_TS_BITS)
+#define DNS_RRL_MAX_TS (DNS_RRL_FOREVER - 1)
+
+#define DNS_RRL_MAX_RESPONSES ((1<<(DNS_RRL_RESPONSE_BITS-1))-1)
+#define DNS_RRL_MAX_WINDOW 3600
+#if DNS_RRL_MAX_WINDOW >= DNS_RRL_MAX_TS
+#error "DNS_RRL_MAX_WINDOW is too large"
+#endif
+#define DNS_RRL_MAX_RATE 1000
+#if DNS_RRL_MAX_RATE >= (DNS_RRL_MAX_RESPONSES / DNS_RRL_MAX_WINDOW)
+#error "DNS_RRL_MAX_rate is too large"
+#endif
+
+#if (1<<DNS_RRL_LOG_BITS) >= DNS_RRL_FOREVER
+#error DNS_RRL_LOG_BITS is too big
+#endif
+#define DNS_RRL_MAX_LOG_SECS 1800
+#if DNS_RRL_MAX_LOG_SECS >= (1<<DNS_RRL_LOG_BITS)
+#error "DNS_RRL_MAX_LOG_SECS is too large"
+#endif
+#define DNS_RRL_STOP_LOG_SECS 60
+#if DNS_RRL_STOP_LOG_SECS >= (1<<DNS_RRL_LOG_BITS)
+#error "DNS_RRL_STOP_LOG_SECS is too large"
+#endif
+
+
+/*
+ * A hash table of rate-limit entries.
+ */
+struct dns_rrl_hash {
+ isc_stdtime_t check_time;
+ unsigned int gen :DNS_RRL_HASH_GEN_BITS;
+ int length;
+ dns_rrl_bin_t bins[1];
+};
+
+/*
+ * A block of rate-limit entries.
+ */
+typedef struct dns_rrl_block dns_rrl_block_t;
+struct dns_rrl_block {
+ ISC_LINK(dns_rrl_block_t) link;
+ int size;
+ dns_rrl_entry_t entries[1];
+};
+
+/*
+ * A rate limited qname buffer.
+ */
+typedef struct dns_rrl_qname_buf dns_rrl_qname_buf_t;
+struct dns_rrl_qname_buf {
+ ISC_LINK(dns_rrl_qname_buf_t) link;
+ const dns_rrl_entry_t *e;
+ unsigned int index;
+ dns_fixedname_t qname;
+};
+
+typedef struct dns_rrl_rate dns_rrl_rate_t;
+struct dns_rrl_rate {
+ int r;
+ int scaled;
+ const char *str;
+};
+
+/*
+ * Per-view query rate limit parameters and a pointer to database.
+ */
+typedef struct dns_rrl dns_rrl_t;
+struct dns_rrl {
+ isc_mutex_t lock;
+ isc_mem_t *mctx;
+
+ bool log_only;
+ dns_rrl_rate_t responses_per_second;
+ dns_rrl_rate_t referrals_per_second;
+ dns_rrl_rate_t nodata_per_second;
+ dns_rrl_rate_t nxdomains_per_second;
+ dns_rrl_rate_t errors_per_second;
+ dns_rrl_rate_t all_per_second;
+ dns_rrl_rate_t slip;
+ int window;
+ double qps_scale;
+ int max_entries;
+
+ dns_acl_t *exempt;
+
+ int num_entries;
+
+ int qps_responses;
+ isc_stdtime_t qps_time;
+ double qps;
+
+ unsigned int probes;
+ unsigned int searches;
+
+ ISC_LIST(dns_rrl_block_t) blocks;
+ ISC_LIST(dns_rrl_entry_t) lru;
+
+ dns_rrl_hash_t *hash;
+ dns_rrl_hash_t *old_hash;
+ unsigned int hash_gen;
+
+ unsigned int ts_gen;
+# define DNS_RRL_TS_BASES (1<<DNS_RRL_TS_GEN_BITS)
+ isc_stdtime_t ts_bases[DNS_RRL_TS_BASES];
+
+ int ipv4_prefixlen;
+ uint32_t ipv4_mask;
+ int ipv6_prefixlen;
+ uint32_t ipv6_mask[4];
+
+ isc_stdtime_t log_stops_time;
+ dns_rrl_entry_t *last_logged;
+ int num_logged;
+ int num_qnames;
+ ISC_LIST(dns_rrl_qname_buf_t) qname_free;
+# define DNS_RRL_QNAMES (1<<DNS_RRL_QNAMES_BITS)
+ dns_rrl_qname_buf_t *qnames[DNS_RRL_QNAMES];
+};
+
+typedef enum {
+ DNS_RRL_RESULT_OK,
+ DNS_RRL_RESULT_DROP,
+ DNS_RRL_RESULT_SLIP,
+} dns_rrl_result_t;
+
+dns_rrl_result_t
+dns_rrl(dns_view_t *view,
+ const isc_sockaddr_t *client_addr, bool is_tcp,
+ dns_rdataclass_t rdclass, dns_rdatatype_t qtype,
+ dns_name_t *qname, isc_result_t resp_result, isc_stdtime_t now,
+ bool wouldlog, char *log_buf, unsigned int log_buf_len);
+
+void
+dns_rrl_view_destroy(dns_view_t *view);
+
+isc_result_t
+dns_rrl_init(dns_rrl_t **rrlp, dns_view_t *view, int min_entries);
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_RRL_H */
diff --git a/lib/dns/include/dns/sdb.h b/lib/dns/include/dns/sdb.h
new file mode 100644
index 0000000..c033b60
--- /dev/null
+++ b/lib/dns/include/dns/sdb.h
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#ifndef DNS_SDB_H
+#define DNS_SDB_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file dns/sdb.h
+ * \brief
+ * Simple database API.
+ */
+
+/***
+ *** Imports
+ ***/
+
+#include <inttypes.h>
+
+#include <isc/lang.h>
+
+#include <dns/clientinfo.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 *lookup,
+ dns_clientinfomethods_t *methods,
+ dns_clientinfo_t *clientinfo);
+typedef isc_result_t
+(*dns_sdblookup2func_t)(const dns_name_t *zone, const dns_name_t *name,
+ void *dbdata, dns_sdblookup_t *lookup,
+ dns_clientinfomethods_t *methods,
+ dns_clientinfo_t *clientinfo);
+
+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_sdblookup2func_t lookup2;
+} dns_sdbmethods_t;
+
+/***
+ *** Functions
+ ***/
+
+ISC_LANG_BEGINDECLS
+
+#define DNS_SDBFLAG_RELATIVEOWNER 0x00000001U
+#define DNS_SDBFLAG_RELATIVERDATA 0x00000002U
+#define DNS_SDBFLAG_THREADSAFE 0x00000004U
+#define DNS_SDBFLAG_DNS64 0x00000008U
+
+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" containing 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,
+ 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..5b6bc3e
--- /dev/null
+++ b/lib/dns/include/dns/sdlz.h
@@ -0,0 +1,374 @@
+/*
+ * Portions Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * 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.
+ */
+
+
+/*! \file dns/sdlz.h */
+
+#ifndef SDLZ_H
+#define SDLZ_H 1
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <dns/clientinfo.h>
+#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 shutting 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,
+ dns_clientinfomethods_t *methods,
+ dns_clientinfo_t *clientinfo);
+/*%<
+ * 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_labels
+ * 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,
+ dns_clientinfomethods_t *methods,
+ dns_clientinfo_t *clientinfo);
+
+/*%<
+ * 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 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.
+ *
+ * The 'methods' and 'clientinfo' args allow an SDLZ driver to retrieve
+ * information about the querying client (such as source IP address)
+ * from the caller.
+ */
+
+typedef isc_result_t (*dns_sdlznewversion_t)(const char *zone,
+ void *driverarg, void *dbdata,
+ void **versionp);
+/*%<
+ * Method prototype. Drivers implementing the SDLZ interface may
+ * supply a newversion method. This method is called to start a
+ * write transaction on a zone and should only be implemented by
+ * writeable backends.
+ * When implemented, the driver should create a new transaction, and
+ * fill *versionp with a pointer to the transaction state. The
+ * closeversion function will be called to close the transaction.
+ */
+
+typedef void (*dns_sdlzcloseversion_t)(const char *zone, bool commit,
+ void *driverarg, void *dbdata,
+ void **versionp);
+/*%<
+ * Method prototype. Drivers implementing the SDLZ interface must
+ * supply a closeversion method if they supply a newversion method.
+ * When implemented, the driver should close the given transaction,
+ * committing changes if 'commit' is true. If 'commit' is not true
+ * then all changes should be discarded and the database rolled back.
+ * If the call is successful then *versionp should be set to NULL
+ */
+
+typedef isc_result_t (*dns_sdlzconfigure_t)(dns_view_t *view,
+ dns_dlzdb_t *dlzdb,
+ void *driverarg, void *dbdata);
+/*%<
+ * Method prototype. Drivers implementing the SDLZ interface may
+ * supply a configure method. When supplied, it will be called
+ * immediately after the create method to give the driver a chance
+ * to configure writeable zones
+ */
+
+
+typedef bool (*dns_sdlzssumatch_t)(const char *signer,
+ const char *name,
+ const char *tcpaddr,
+ const char *type,
+ const char *key,
+ uint32_t keydatalen,
+ unsigned char *keydata,
+ void *driverarg,
+ void *dbdata);
+
+/*%<
+ * Method prototype. Drivers implementing the SDLZ interface may
+ * supply a ssumatch method. If supplied, then ssumatch will be
+ * called to authorize any zone updates. The driver should return
+ * true to allow the update, and false to deny it. For a DLZ
+ * controlled zone, this is the only access control on updates.
+ */
+
+
+typedef isc_result_t (*dns_sdlzmodrdataset_t)(const char *name,
+ const char *rdatastr,
+ void *driverarg, void *dbdata,
+ void *version);
+/*%<
+ * Method prototype. Drivers implementing the SDLZ interface may
+ * supply addrdataset and subtractrdataset methods. If supplied, then these
+ * will be called when rdatasets are added/subtracted during
+ * updates. The version parameter comes from a call to the sdlz
+ * newversion() method from the driver. The rdataset parameter is a
+ * linearise string representation of the rdataset change. The format
+ * is the same as used by dig when displaying records. The fields are
+ * tab delimited.
+ */
+
+typedef isc_result_t (*dns_sdlzdelrdataset_t)(const char *name,
+ const char *type,
+ void *driverarg, void *dbdata,
+ void *version);
+/*%<
+ * Method prototype. Drivers implementing the SDLZ interface may
+ * supply a delrdataset method. If supplied, then this
+ * function will be called when rdatasets are deleted during
+ * updates. The call should remove all rdatasets of the given type for
+ * the specified name.
+ */
+
+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_sdlznewversion_t newversion;
+ dns_sdlzcloseversion_t closeversion;
+ dns_sdlzconfigure_t configure;
+ dns_sdlzssumatch_t ssumatch;
+ dns_sdlzmodrdataset_t addrdataset;
+ dns_sdlzmodrdataset_t subtractrdataset;
+ dns_sdlzdelrdataset_t delrdataset;
+} 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.
+ */
+
+typedef isc_result_t dns_sdlz_putnamedrr_t(dns_sdlzallnodes_t *allnodes,
+ const char *name,
+ const char *type,
+ dns_ttl_t ttl,
+ const char *data);
+dns_sdlz_putnamedrr_t dns_sdlz_putnamedrr;
+
+/*%<
+ * Add a single resource record to the allnodes structure to be later
+ * parsed into a zone transfer response.
+ */
+
+typedef isc_result_t dns_sdlz_putrr_t(dns_sdlzlookup_t *lookup,
+ const char *type,
+ dns_ttl_t ttl,
+ const char *data);
+dns_sdlz_putrr_t dns_sdlz_putrr;
+/*%<
+ * Add a single resource record to the lookup structure to be later
+ * parsed into a query response.
+ */
+
+typedef isc_result_t dns_sdlz_putsoa_t(dns_sdlzlookup_t *lookup,
+ const char *mname,
+ const char *rname,
+ uint32_t serial);
+dns_sdlz_putsoa_t dns_sdlz_putsoa;
+/*%<
+ * 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.
+ */
+
+
+typedef isc_result_t dns_sdlz_setdb_t(dns_dlzdb_t *dlzdatabase,
+ dns_rdataclass_t rdclass,
+ dns_name_t *name,
+ dns_db_t **dbp);
+dns_sdlz_setdb_t dns_sdlz_setdb;
+/*%<
+ * Create the database pointers for a writeable SDLZ zone
+ */
+
+
+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..e27d6b4
--- /dev/null
+++ b/lib/dns/include/dns/secalg.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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
+ */
+
+#define DNS_SECALG_FORMATSIZE 20
+void
+dns_secalg_format(dns_secalg_t alg, char *cp, unsigned int size);
+/*%<
+ * Wrapper for dns_secalg_totext(), writing text into 'cp'
+ */
+
+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..7ee8958
--- /dev/null
+++ b/lib/dns/include/dns/secproto.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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..1e97e7a
--- /dev/null
+++ b/lib/dns/include/dns/soa.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#ifndef DNS_SOA_H
+#define DNS_SOA_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file dns/soa.h
+ * \brief
+ * SOA utilities.
+ */
+
+/***
+ *** Imports
+ ***/
+
+#include <inttypes.h>
+
+#include <isc/lang.h>
+#include <isc/types.h>
+
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+#define DNS_SOA_BUFFERSIZE ((2 * DNS_NAME_MAXWIRE) + (4 * 5))
+
+isc_result_t
+dns_soa_buildrdata(dns_name_t *origin, dns_name_t *contact,
+ dns_rdataclass_t rdclass,
+ uint32_t serial, uint32_t refresh,
+ uint32_t retry, uint32_t expire,
+ uint32_t minimum, unsigned char *buffer,
+ dns_rdata_t *rdata);
+/*%<
+ * Build the rdata of an SOA record.
+ *
+ * Requires:
+ *\li buffer Points to a temporary buffer of at least
+ * DNS_SOA_BUFFERSIZE bytes.
+ *\li rdata Points to an initialized dns_rdata_t.
+ *
+ * Ensures:
+ * \li *rdata Contains a valid SOA rdata. The 'data' member
+ * refers to 'buffer'.
+ */
+
+uint32_t
+dns_soa_getserial(dns_rdata_t *rdata);
+uint32_t
+dns_soa_getrefresh(dns_rdata_t *rdata);
+uint32_t
+dns_soa_getretry(dns_rdata_t *rdata);
+uint32_t
+dns_soa_getexpire(dns_rdata_t *rdata);
+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(uint32_t val, dns_rdata_t *rdata);
+void
+dns_soa_setrefresh(uint32_t val, dns_rdata_t *rdata);
+void
+dns_soa_setretry(uint32_t val, dns_rdata_t *rdata);
+void
+dns_soa_setexpire(uint32_t val, dns_rdata_t *rdata);
+void
+dns_soa_setminimum(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..402583c
--- /dev/null
+++ b/lib/dns/include/dns/ssu.h
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef DNS_SSU_H
+#define DNS_SSU_H 1
+
+/*! \file dns/ssu.h */
+
+#include <stdbool.h>
+
+#include <isc/lang.h>
+
+#include <dns/acl.h>
+#include <dns/types.h>
+#include <dst/dst.h>
+
+ISC_LANG_BEGINDECLS
+
+typedef enum {
+ dns_ssumatchtype_name = 0,
+ dns_ssumatchtype_subdomain = 1,
+ dns_ssumatchtype_wildcard = 2,
+ dns_ssumatchtype_self = 3,
+ dns_ssumatchtype_selfsub = 4,
+ dns_ssumatchtype_selfwild = 5,
+ dns_ssumatchtype_selfkrb5 = 6,
+ dns_ssumatchtype_selfms = 7,
+ dns_ssumatchtype_subdomainms = 8,
+ dns_ssumatchtype_subdomainkrb5 = 9,
+ dns_ssumatchtype_tcpself = 10,
+ dns_ssumatchtype_6to4self = 11,
+ dns_ssumatchtype_external = 12,
+ dns_ssumatchtype_local = 13,
+ dns_ssumatchtype_selfsubms = 14,
+ dns_ssumatchtype_selfsubkrb5 = 15,
+ dns_ssumatchtype_max = 15, /* max value */
+
+ dns_ssumatchtype_dlz = 16 /* intentionally higher than _max */
+} dns_ssumatchtype_t;
+
+#define DNS_SSUMATCHTYPE_NAME dns_ssumatchtype_name
+#define DNS_SSUMATCHTYPE_SUBDOMAIN dns_ssumatchtype_subdomain
+#define DNS_SSUMATCHTYPE_WILDCARD dns_ssumatchtype_wildcard
+#define DNS_SSUMATCHTYPE_SELF dns_ssumatchtype_self
+#define DNS_SSUMATCHTYPE_SELFSUB dns_ssumatchtype_selfsub
+#define DNS_SSUMATCHTYPE_SELFWILD dns_ssumatchtype_selfwild
+#define DNS_SSUMATCHTYPE_SELFKRB5 dns_ssumatchtype_selfkrb5
+#define DNS_SSUMATCHTYPE_SELFMS dns_ssumatchtype_selfms
+#define DNS_SSUMATCHTYPE_SUBDOMAINMS dns_ssumatchtype_subdomainms
+#define DNS_SSUMATCHTYPE_SUBDOMAINKRB5 dns_ssumatchtype_subdomainkrb5
+#define DNS_SSUMATCHTYPE_TCPSELF dns_ssumatchtype_tcpself
+#define DNS_SSUMATCHTYPE_6TO4SELF dns_ssumatchtype_6to4self
+#define DNS_SSUMATCHTYPE_EXTERNAL dns_ssumatchtype_external
+#define DNS_SSUMATCHTYPE_LOCAL dns_ssumatchtype_local
+#define DNS_SSUMATCHTYPE_MAX dns_ssumatchtype_max /* max value */
+
+#define DNS_SSUMATCHTYPE_DLZ dns_ssumatchtype_dlz /* intentionally higher than _MAX */
+
+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
+ */
+
+isc_result_t
+dns_ssutable_createdlz(isc_mem_t *mctx, dns_ssutable_t **tablep,
+ dns_dlzdb_t *dlzdatabase);
+/*%<
+ * Create an SSU table that contains a dlzdatabase pointer, and a
+ * single rule with matchtype DNS_SSUMATCHTYPE_DLZ. This type of SSU
+ * table is used by writeable DLZ drivers to offload authorization for
+ * updates to the driver.
+ */
+
+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, bool 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
+ */
+
+bool
+dns_ssutable_checkrules(dns_ssutable_t *table, dns_name_t *signer,
+ dns_name_t *name, isc_netaddr_t *addr,
+ dns_rdatatype_t type, const dst_key_t *key);
+bool
+dns_ssutable_checkrules2(dns_ssutable_t *table, dns_name_t *signer,
+ dns_name_t *name, isc_netaddr_t *addr,
+ bool tcp, const dns_aclenv_t *env,
+ dns_rdatatype_t type, const dst_key_t *key);
+/*%<
+ * 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:
+ * In dns_ssutable_checkrules(), 'addr' should only be
+ * set if the request received via TCP. This provides a
+ * weak assurance that the request was not spoofed.
+ * 'addr' is to to validate DNS_SSUMATCHTYPE_TCPSELF
+ * and DNS_SSUMATCHTYPE_6TO4SELF rules.
+ *
+ * In dns_ssutable_checkrules2(), 'addr' can also be passed for
+ * UDP requests and TCP is specified via the 'tcp' parameter.
+ * In addition to DNS_SSUMATCHTYPE_TCPSELF and
+ * tcp_ssumatchtype_6to4self rules, the address
+ * also be used to check DNS_SSUMATCHTYPE_LOCAL rules.
+ * If 'addr' is set then 'env' must also be set so that
+ * requests from non-localhost addresses can be rejected.
+ *
+ * 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 'addr' is NULL or a valid network address.
+ *\li 'aclenv' is NULL or a valid ACL environment.
+ *\li 'name' is a valid absolute name
+ *\li if 'addr' is not NULL, 'env' is not NULL.
+ */
+
+
+/*% Accessor functions to extract rule components */
+bool 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
+ */
+
+bool
+dns_ssu_external_match(dns_name_t *identity, dns_name_t *signer,
+ dns_name_t *name, isc_netaddr_t *tcpaddr,
+ dns_rdatatype_t type, const dst_key_t *key,
+ isc_mem_t *mctx);
+/*%<
+ * Check a policy rule via an external application
+ */
+
+isc_result_t
+dns_ssu_mtypefromstring(const char *str, dns_ssumatchtype_t *mtype);
+/*%<
+ * Set 'mtype' from 'str'
+ *
+ * Requires:
+ *\li 'str' is not NULL.
+ *\li 'mtype' is not NULL,
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOTFOUND
+ */
+
+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..9ef47af
--- /dev/null
+++ b/lib/dns/include/dns/stats.h
@@ -0,0 +1,466 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $Id$ */
+
+#ifndef DNS_STATS_H
+#define DNS_STATS_H 1
+
+/*! \file dns/stats.h */
+
+#include <inttypes.h>
+
+#include <dns/types.h>
+
+/*%
+ * Statistics counters. Used as isc_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_dispabort = 21,
+ dns_resstatscounter_dispsockfail = 22,
+ dns_resstatscounter_querytimeout = 23,
+ dns_resstatscounter_queryrtt0 = 24,
+ dns_resstatscounter_queryrtt1 = 25,
+ dns_resstatscounter_queryrtt2 = 26,
+ dns_resstatscounter_queryrtt3 = 27,
+ dns_resstatscounter_queryrtt4 = 28,
+ dns_resstatscounter_queryrtt5 = 29,
+ dns_resstatscounter_nfetch = 30,
+ dns_resstatscounter_disprequdp = 31,
+ dns_resstatscounter_dispreqtcp = 32,
+ dns_resstatscounter_buckets = 33,
+ dns_resstatscounter_refused = 34,
+ dns_resstatscounter_cookienew = 35,
+ dns_resstatscounter_cookieout = 36,
+ dns_resstatscounter_cookiein = 37,
+ dns_resstatscounter_cookieok = 38,
+ dns_resstatscounter_badvers = 39,
+ dns_resstatscounter_badcookie = 40,
+ dns_resstatscounter_zonequota = 41,
+ dns_resstatscounter_serverquota = 42,
+ dns_resstatscounter_nextitem = 43,
+ dns_resstatscounter_max = 44,
+
+ /*
+ * DNSSEC stats.
+ */
+ dns_dnssecstats_asis = 0,
+ dns_dnssecstats_downcase = 1,
+ dns_dnssecstats_wildcard = 2,
+ dns_dnssecstats_fail = 3,
+
+ dns_dnssecstats_max = 4,
+
+ /*%
+ * 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,
+
+ /*
+ * Adb statistics values.
+ */
+ dns_adbstats_nentries = 0,
+ dns_adbstats_entriescnt = 1,
+ dns_adbstats_nnames = 2,
+ dns_adbstats_namescnt = 3,
+
+ dns_adbstats_max = 4,
+
+ /*
+ * Cache statistics values.
+ */
+ dns_cachestatscounter_hits = 1,
+ dns_cachestatscounter_misses = 2,
+ dns_cachestatscounter_queryhits = 3,
+ dns_cachestatscounter_querymisses = 4,
+ dns_cachestatscounter_deletelru = 5,
+ dns_cachestatscounter_deletettl = 6,
+
+ dns_cachestatscounter_max = 7,
+
+ /*%
+ * 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) */
+
+ /*%
+ * DNSTAP statistics counters.
+ */
+ dns_dnstapcounter_success = 0,
+ dns_dnstapcounter_drop = 1,
+ dns_dnstapcounter_max = 2
+};
+
+#define DNS_STATS_NCOUNTERS 8
+
+#if 0
+/*%<
+ * Flag(s) for dns_xxxstats_dump(). DNS_STATSDUMP_VERBOSE is obsolete.
+ * ISC_STATSDUMP_VERBOSE should be used instead. These two values are
+ * intentionally defined to be the same value to ensure binary compatibility.
+ */
+#define DNS_STATSDUMP_VERBOSE 0x00000001 /*%< dump 0-value counters */
+#endif
+
+/*%<
+ * (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.
+ *
+ * _STALE
+ * RRset type counters only. This indicates a record that marked for
+ * removal.
+ *
+ * Note: incrementing _STALE will decrement the corresponding non-stale
+ * counter.
+ */
+#define DNS_RDATASTATSTYPE_ATTR_OTHERTYPE 0x0001
+#define DNS_RDATASTATSTYPE_ATTR_NXRRSET 0x0002
+#define DNS_RDATASTATSTYPE_ATTR_NXDOMAIN 0x0004
+#define DNS_RDATASTATSTYPE_ATTR_STALE 0x0008
+
+/*%<
+ * Conversion macros among dns_rdatatype_t, attributes and isc_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)(isc_statscounter_t, uint64_t,
+ void *);
+typedef void (*dns_rdatatypestats_dumper_t)(dns_rdatastatstype_t, uint64_t,
+ void *);
+typedef void (*dns_opcodestats_dumper_t)(dns_opcode_t, uint64_t, void *);
+
+typedef void (*dns_rcodestats_dumper_t)(dns_rcode_t, uint64_t, void *);
+
+ISC_LANG_BEGINDECLS
+
+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.
+ * This function is obsolete. A more general function, isc_stats_create(),
+ * should be used.
+ *
+ * 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
+ */
+
+isc_result_t
+dns_rcodestats_create(isc_mem_t *mctx, dns_stats_t **statsp);
+/*%<
+ * Create a statistics counter structure per assigned rcode.
+ *
+ * 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, isc_statscounter_t counter);
+/*%<
+ * Increment the counter-th counter of stats. This function is obsolete.
+ * A more general function, isc_stats_increment(), should be used.
+ *
+ * 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'.
+ *
+ * Note: if 'rrsettype' has the _STALE attribute set the corresponding
+ * non-stale counter will be decremented.
+ *
+ * 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_rcodestats_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_rcodestats_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 ISC_STATSDUMP_VERBOSE flag, even such counters are dumped.
+ *
+ * This function is obsolete. A more general function, isc_stats_dump(),
+ * should be used.
+ *
+ * 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 ISC_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 ISC_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 ISC_STATSDUMP_VERBOSE flag, even
+ * such counters are dumped.
+ *
+ * Requires:
+ *\li 'stats' is a valid dns_stats_t created by dns_generalstats_create().
+ */
+
+void
+dns_rcodestats_dump(dns_stats_t *stats, dns_rcodestats_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 rcode, the current
+ * counter value and the given argument arg. By default counters that have a
+ * value of 0 is skipped; if options has the ISC_STATSDUMP_VERBOSE flag, even
+ * such counters are dumped.
+ *
+ * Requires:
+ *\li 'stats' is a valid dns_stats_t created by dns_generalstats_create().
+ */
+
+isc_result_t
+dns_stats_alloccounters(isc_mem_t *mctx, 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, 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..1e8f29b
--- /dev/null
+++ b/lib/dns/include/dns/tcpmsg.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#ifndef DNS_TCPMSG_H
+#define DNS_TCPMSG_H 1
+
+/*! \file dns/tcpmsg.h */
+
+#include <inttypes.h>
+
+#include <isc/buffer.h>
+#include <isc/lang.h>
+#include <isc/socket.h>
+
+typedef struct dns_tcpmsg {
+ /* private (don't touch!) */
+ unsigned int magic;
+ 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..012b733
--- /dev/null
+++ b/lib/dns/include/dns/time.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#ifndef DNS_TIME_H
+#define DNS_TIME_H 1
+
+/*! \file dns/time.h */
+
+/***
+ *** Imports
+ ***/
+
+#include <inttypes.h>
+
+#include <isc/buffer.h>
+#include <isc/lang.h>
+
+ISC_LANG_BEGINDECLS
+
+/***
+ *** Functions
+ ***/
+
+isc_result_t
+dns_time64_fromtext(const char *source, 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, uint32_t *target);
+/*%<
+ * Like dns_time64_fromtext, but returns the second count modulo 2^32
+ * as per RFC2535.
+ */
+
+
+isc_result_t
+dns_time64_totext(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(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.
+ */
+
+int64_t
+dns_time64_from32(uint32_t value);
+/*%<
+ * Covert a 32-bit cyclic time value into a 64 bit time stamp.
+ */
+
+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..845cf6c
--- /dev/null
+++ b/lib/dns/include/dns/timer.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#ifndef DNS_TIMER_H
+#define DNS_TIMER_H 1
+
+/*! \file dns/timer.h */
+
+/***
+ *** Imports
+ ***/
+
+#include <stdbool.h>
+
+#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, bool 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 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..f2500fa
--- /dev/null
+++ b/lib/dns/include/dns/tkey.h
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#ifndef DNS_TKEY_H
+#define DNS_TKEY_H 1
+
+/*! \file dns/tkey.h */
+
+#include <inttypes.h>
+#include <stdbool.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;
+ char *gssapi_keytab;
+};
+
+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,
+ 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, uint32_t lifetime,
+ gss_ctx_id_t *context, bool win2k,
+ isc_mem_t *mctx, char **err_message);
+/*%<
+ * 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
+ *\li *err_message optional error 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, char **err_message);
+/*%<
+ * 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,
+ bool win2k, char **err_message);
+
+/*
+ * Client side negotiation of GSS-TSIG. Process the response
+ * 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/tsec.h b/lib/dns/include/dns/tsec.h
new file mode 100644
index 0000000..95e16c8
--- /dev/null
+++ b/lib/dns/include/dns/tsec.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#ifndef DNS_TSEC_H
+#define DNS_TSEC_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file
+ *
+ * \brief
+ * The TSEC (Transaction Security) module is an abstraction layer for managing
+ * DNS transaction mechanisms such as TSIG or SIG(0). A TSEC structure is a
+ * mechanism-independent object containing key information specific to the
+ * mechanism, and is expected to be used as an argument to other modules
+ * that use transaction security in a mechanism-independent manner.
+ *
+ * MP:
+ *\li A TSEC structure is expected to be thread-specific. No inter-thread
+ * synchronization is ensured in multiple access to a single TSEC
+ * structure.
+ *
+ * Resources:
+ *\li TBS
+ *
+ * Security:
+ *\li This module does not handle any low-level data directly, and so no
+ * security issue specific to this module is anticipated.
+ */
+
+#include <dns/types.h>
+
+#include <dst/dst.h>
+
+ISC_LANG_BEGINDECLS
+
+/***
+ *** Types
+ ***/
+
+/*%
+ * Transaction security types.
+ */
+typedef enum {
+ dns_tsectype_none,
+ dns_tsectype_tsig,
+ dns_tsectype_sig0
+} dns_tsectype_t;
+
+isc_result_t
+dns_tsec_create(isc_mem_t *mctx, dns_tsectype_t type, dst_key_t *key,
+ dns_tsec_t **tsecp);
+/*%<
+ * Create a TSEC structure and stores a type-dependent key structure in it.
+ * For a TSIG key (type is dns_tsectype_tsig), dns_tsec_create() creates a
+ * TSIG key structure from '*key' and keeps it in the structure. For other
+ * types, this function simply retains '*key' in the structure. In either
+ * case, the ownership of '*key' is transferred to the TSEC module; the caller
+ * must not modify or destroy it after the call to dns_tsec_create().
+ *
+ * Requires:
+ *
+ *\li 'mctx' is a valid memory context.
+ *
+ *\li 'type' is a valid value of dns_tsectype_t (see above).
+ *
+ *\li 'key' is a valid key.
+ *
+ *\li tsecp != NULL && *tsecp == NULL.
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS On success.
+ *
+ *\li Anything else Failure.
+ */
+
+void
+dns_tsec_destroy(dns_tsec_t **tsecp);
+/*%<
+ * Destroy the TSEC structure. The stored key is also detached or destroyed.
+ *
+ * Requires
+ *
+ *\li '*tsecp' is a valid TSEC structure.
+ *
+ * Ensures
+ *
+ *\li *tsecp == NULL.
+ *
+ */
+
+dns_tsectype_t
+dns_tsec_gettype(dns_tsec_t *tsec);
+/*%<
+ * Return the TSEC type of '*tsec'.
+ *
+ * Requires
+ *
+ *\li 'tsec' is a valid TSEC structure.
+ *
+ */
+
+void
+dns_tsec_getkey(dns_tsec_t *tsec, void *keyp);
+/*%<
+ * Return the TSEC key of '*tsec' in '*keyp'.
+ *
+ * Requires
+ *
+ *\li keyp != NULL
+ *
+ * Ensures
+ *
+ *\li *tsecp points to a valid key structure depending on the TSEC type.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_TSEC_H */
diff --git a/lib/dns/include/dns/tsig.h b/lib/dns/include/dns/tsig.h
new file mode 100644
index 0000000..a7b7bc2
--- /dev/null
+++ b/lib/dns/include/dns/tsig.h
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#ifndef DNS_TSIG_H
+#define DNS_TSIG_H 1
+
+/*! \file dns/tsig.h */
+
+#include <stdbool.h>
+
+#include <isc/lang.h>
+#include <isc/refcount.h>
+#include <isc/rwlock.h>
+#include <isc/stdio.h>
+#include <isc/stdtime.h>
+
+#include <pk11/site.h>
+
+#include <dns/types.h>
+#include <dns/name.h>
+
+#include <dst/dst.h>
+
+/*
+ * Algorithms.
+ */
+#ifndef PK11_MD5_DISABLE
+LIBDNS_EXTERNAL_DATA extern dns_name_t *dns_tsig_hmacmd5_name;
+#define DNS_TSIG_HMACMD5_NAME dns_tsig_hmacmd5_name
+#endif
+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;
+ /*
+ * LRU list of generated key along with a count of the keys on the
+ * list and a maximum size.
+ */
+ unsigned int generated;
+ unsigned int maxgenerated;
+ ISC_LIST(dns_tsigkey_t) lru;
+ unsigned int references;
+};
+
+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 */
+ bool 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 */
+ ISC_LINK(dns_tsigkey_t) link;
+};
+
+#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, bool 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, bool 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.
+ *
+ * If dns_tsigkey_createfromkey is successful a new reference to 'dstkey'
+ * will have been made.
+ *
+ * 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 'dstkey' 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
+ */
+
+isc_result_t
+dns_tsigkeyring_add(dns_tsig_keyring_t *ring, dns_name_t *name,
+ dns_tsigkey_t *tkey);
+/*%<
+ * Place a TSIG key onto a key ring.
+ *
+ * Requires:
+ *\li 'ring', 'name' and 'tkey' are not NULL
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li Any other value indicates failure.
+ */
+
+
+void
+dns_tsigkeyring_attach(dns_tsig_keyring_t *source, dns_tsig_keyring_t **target);
+
+void
+dns_tsigkeyring_detach(dns_tsig_keyring_t **ringp);
+
+isc_result_t
+dns_tsigkeyring_dumpanddetach(dns_tsig_keyring_t **ringp, FILE *fp);
+
+/*%<
+ * Destroy a TSIG key ring.
+ *
+ * Requires:
+ *\li 'ringp' is not NULL
+ */
+
+void
+dns_keyring_restore(dns_tsig_keyring_t *ring, FILE *fp);
+
+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..9241f10
--- /dev/null
+++ b/lib/dns/include/dns/ttl.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#ifndef DNS_TTL_H
+#define DNS_TTL_H 1
+
+/*! \file dns/ttl.h */
+
+/***
+ *** Imports
+ ***/
+
+#include <stdbool.h>
+
+#include <isc/lang.h>
+#include <isc/types.h>
+
+ISC_LANG_BEGINDECLS
+
+/***
+ *** Functions
+ ***/
+
+isc_result_t
+dns_ttl_totext(uint32_t src, bool verbose,
+ isc_buffer_t *target);
+isc_result_t
+dns_ttl_totext2(uint32_t src, bool verbose,
+ bool upcase, 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 false, use the terse BIND 8 style, like "1w2d3h4m5s".
+ *
+ * If 'verbose' is true, use a verbose style like the SOA comments
+ * in "dig", like "1 week 2 days 3 hours 4 minutes 5 seconds".
+ *
+ * If 'upcase' is true, we conform to the BIND 8 style in which
+ * the unit letter is capitalized if there is only a single unit
+ * letter to print (for example, "1m30s", but "2M")
+ *
+ * If 'upcase' is false, unit letters are always in lower case.
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS
+ * \li ISC_R_NOSPACE
+ */
+
+isc_result_t
+dns_counter_fromtext(isc_textregion_t *source, 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, 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..6754d16
--- /dev/null
+++ b/lib/dns/include/dns/types.h
@@ -0,0 +1,437 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#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 <stdio.h>
+#include <inttypes.h>
+#include <stdbool.h>
+
+#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_badcache dns_badcache_t;
+typedef struct dns_byaddr dns_byaddr_t;
+typedef struct dns_catz_zonemodmethods dns_catz_zonemodmethods_t;
+typedef struct dns_catz_entry_options dns_catz_options_t;
+typedef struct dns_catz_entry dns_catz_entry_t;
+typedef struct dns_catz_zone dns_catz_zone_t;
+typedef struct dns_catz_changed dns_catz_changed_t;
+typedef struct dns_catz_zones dns_catz_zones_t;
+typedef struct dns_client dns_client_t;
+typedef void dns_clientrestrans_t;
+typedef void dns_clientreqtrans_t;
+typedef void dns_clientupdatetrans_t;
+typedef struct dns_cache dns_cache_t;
+typedef 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_dbonupdatelistener dns_dbonupdatelistener_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 ISC_LIST(dns_dlzdb_t) dns_dlzdblist_t;
+typedef struct dns_dyndbctx dns_dyndbctx_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_dispatchset dns_dispatchset_t;
+typedef struct dns_dispatchmgr dns_dispatchmgr_t;
+typedef struct dns_dispentry dns_dispentry_t;
+typedef struct dns_dns64 dns_dns64_t;
+typedef ISC_LIST(dns_dns64_t) dns_dns64list_t;
+typedef struct dns_dnsseckey dns_dnsseckey_t;
+typedef ISC_LIST(dns_dnsseckey_t) dns_dnsseckeylist_t;
+typedef uint8_t dns_dsdigest_t;
+typedef struct dns_dtdata dns_dtdata_t;
+typedef struct dns_dtenv dns_dtenv_t;
+typedef struct dns_dtmsg dns_dtmsg_t;
+typedef uint16_t dns_dtmsgtype_t;
+typedef struct dns_dumpctx dns_dumpctx_t;
+typedef struct dns_ednsopt dns_ednsopt_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_forwarder dns_forwarder_t;
+typedef struct dns_fwdtable dns_fwdtable_t;
+typedef struct dns_iptable dns_iptable_t;
+typedef uint32_t dns_iterations_t;
+typedef uint16_t dns_keyflags_t;
+typedef struct dns_keynode dns_keynode_t;
+typedef ISC_LIST(dns_keynode_t) dns_keynodelist_t;
+typedef struct dns_keytable dns_keytable_t;
+typedef uint16_t dns_keytag_t;
+typedef struct dns_loadctx dns_loadctx_t;
+typedef struct dns_loadmgr dns_loadmgr_t;
+typedef struct dns_masterrawheader dns_masterrawheader_t;
+typedef uint64_t dns_masterstyle_flags_t;
+typedef struct dns_message dns_message_t;
+typedef 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 struct dns_nta dns_nta_t;
+typedef struct dns_ntatable dns_ntatable_t;
+typedef 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 uint16_t dns_rcode_t;
+typedef struct dns_rdata dns_rdata_t;
+typedef struct dns_rdatacallbacks dns_rdatacallbacks_t;
+typedef 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 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 uint8_t dns_secalg_t;
+typedef 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 uint32_t dns_rdatastatstype_t;
+typedef struct dns_tkeyctx dns_tkeyctx_t;
+typedef uint16_t dns_trust_t;
+typedef struct dns_tsec dns_tsec_t;
+typedef struct dns_tsig_keyring dns_tsig_keyring_t;
+typedef struct dns_tsigkey dns_tsigkey_t;
+typedef uint32_t dns_ttl_t;
+typedef struct dns_update_state dns_update_state_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;
+typedef struct dns_ipkeylist dns_ipkeylist_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_minimal_no = 0,
+ dns_minimal_yes = 1,
+ dns_minimal_noauth = 2,
+ dns_minimal_noauthrec = 3
+} dns_minimaltype_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_map = 3
+} dns_masterformat_t;
+
+typedef enum {
+ dns_aaaa_ok = 0,
+ dns_aaaa_filter = 1,
+ dns_aaaa_break_dnssec = 2
+} dns_aaaa_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)
+ dns_rcode_badcookie = 23
+#define dns_rcode_badcookie ((dns_rcode_t)dns_rcode_badcookie)
+ /*
+ * Update dns_rcodestats_create() and dns_rcodestats_increment()
+ * and this comment if a rcode > dns_rcode_badcookie is assigned.
+ */
+ /* Private space [3841..4095] */
+};
+
+/*%
+ * 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_additional (from the additional section).
+ */
+ dns_trust_pending_additional = 1,
+#define dns_trust_pending_additional \
+ ((dns_trust_t)dns_trust_pending_additional)
+
+ dns_trust_pending_answer = 2,
+#define dns_trust_pending_answer ((dns_trust_t)dns_trust_pending_answer)
+
+ /*% Received in the additional section of a response. */
+ dns_trust_additional = 3,
+#define dns_trust_additional ((dns_trust_t)dns_trust_additional)
+
+ /* Received in a referral response. */
+ dns_trust_glue = 4,
+#define dns_trust_glue ((dns_trust_t)dns_trust_glue)
+
+ /* Answer from a non-authoritative server */
+ dns_trust_answer = 5,
+#define dns_trust_answer ((dns_trust_t)dns_trust_answer)
+
+ /* Received in the authority section as part of an
+ authoritative response */
+ dns_trust_authauthority = 6,
+#define dns_trust_authauthority ((dns_trust_t)dns_trust_authauthority)
+
+ /* Answer from an authoritative server */
+ dns_trust_authanswer = 7,
+#define dns_trust_authanswer ((dns_trust_t)dns_trust_authanswer)
+
+ /* Successfully DNSSEC validated */
+ dns_trust_secure = 8,
+#define dns_trust_secure ((dns_trust_t)dns_trust_secure)
+
+ /* This server is authoritative */
+ dns_trust_ultimate = 9
+#define dns_trust_ultimate ((dns_trust_t)dns_trust_ultimate)
+};
+
+#define DNS_TRUST_PENDING(x) ((x) == dns_trust_pending_answer || \
+ (x) == dns_trust_pending_additional)
+#define DNS_TRUST_ADDITIONAL(x) ((x) == dns_trust_additional || \
+ (x) == dns_trust_pending_additional)
+#define DNS_TRUST_GLUE(x) ((x) == dns_trust_glue)
+#define DNS_TRUST_ANSWER(x) ((x) == dns_trust_answer)
+
+
+/*%
+ * Name checking severities.
+ */
+typedef enum {
+ dns_severity_ignore,
+ dns_severity_warn,
+ dns_severity_fail
+} dns_severity_t;
+
+/*%
+ * DNS Serial Number Update Method.
+ *
+ * \li _none: Keep the current serial.
+ * \li _increment: Add one to the current serial, skipping 0.
+ * \li _unixtime: Set to the seconds since 00:00 Jan 1, 1970,
+ * if possible.
+ * \li _date: Set to today's date in YYYYMMDDVV format:
+ * (Year, Month, Day, Version)
+ */
+typedef enum {
+ dns_updatemethod_none = 0,
+ dns_updatemethod_increment,
+ dns_updatemethod_unixtime,
+ dns_updatemethod_date
+} dns_updatemethod_t;
+
+/*
+ * Functions.
+ */
+typedef void
+(*dns_dumpdonefunc_t)(void *, isc_result_t);
+
+typedef void
+(*dns_loaddonefunc_t)(void *, isc_result_t);
+
+typedef void
+(*dns_rawdatafunc_t)(dns_zone_t *, dns_masterrawheader_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 bool
+(*dns_checkmxfunc_t)(dns_zone_t *, dns_name_t *, dns_name_t *);
+
+typedef bool
+(*dns_checksrvfunc_t)(dns_zone_t *, dns_name_t *, dns_name_t *);
+
+typedef bool
+(*dns_checknsfunc_t)(dns_zone_t *, dns_name_t *, dns_name_t *,
+ dns_rdataset_t *, dns_rdataset_t *);
+
+typedef bool
+(*dns_isselffunc_t)(dns_view_t *, dns_tsigkey_t *, isc_sockaddr_t *,
+ isc_sockaddr_t *, dns_rdataclass_t, void *);
+
+typedef isc_result_t
+(*dns_deserializefunc_t)(void *, FILE *, off_t);
+
+typedef void
+(*dns_nseclog_t)(void *val, int , const char *, ...);
+
+#endif /* DNS_TYPES_H */
diff --git a/lib/dns/include/dns/update.h b/lib/dns/include/dns/update.h
new file mode 100644
index 0000000..6700c8f
--- /dev/null
+++ b/lib/dns/include/dns/update.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#ifndef DNS_UPDATE_H
+#define DNS_UPDATE_H 1
+
+/*! \file dns/update.h */
+
+/***
+ *** Imports
+ ***/
+
+#include <inttypes.h>
+
+#include <isc/lang.h>
+
+#include <dns/types.h>
+#include <dns/diff.h>
+
+typedef struct {
+ void (*func)(void *arg, dns_zone_t *zone, int level,
+ const char *message);
+ void *arg;
+} dns_update_log_t;
+
+ISC_LANG_BEGINDECLS
+
+/***
+ *** Functions
+ ***/
+
+uint32_t
+dns_update_soaserial(uint32_t serial, dns_updatemethod_t method);
+/*%<
+ * Return the next serial number after 'serial', depending on the
+ * update method 'method':
+ *
+ *\li * dns_updatemethod_increment increments the serial number by one
+ *\li * dns_updatemethod_unixtime sets the serial number to the current
+ * time (seconds since UNIX epoch) if possible, or increments by one
+ * if not.
+ */
+
+isc_result_t
+dns_update_signatures(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db,
+ dns_dbversion_t *oldver, dns_dbversion_t *newver,
+ dns_diff_t *diff, uint32_t sigvalidityinterval);
+
+isc_result_t
+dns_update_signaturesinc(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db,
+ dns_dbversion_t *oldver, dns_dbversion_t *newver,
+ dns_diff_t *diff, uint32_t sigvalidityinterval,
+ dns_update_state_t **state);
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_UPDATE_H */
diff --git a/lib/dns/include/dns/validator.h b/lib/dns/include/dns/validator.h
new file mode 100644
index 0000000..81e46d8
--- /dev/null
+++ b/lib/dns/include/dns/validator.h
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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 <stdbool.h>
+
+#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 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.
+ */
+ bool optout;
+ /*
+ * Answer is secure.
+ */
+ bool secure;
+} 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;
+ bool 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;
+ bool havedlvsep;
+ bool mustbesecure;
+ unsigned int dlvlabels;
+ unsigned int depth;
+ unsigned int authcount;
+ unsigned int authfail;
+ isc_stdtime_t start;
+};
+
+/*%
+ * dns_validator_create() options.
+ */
+#define DNS_VALIDATOR_DLV 0x0001U
+#define DNS_VALIDATOR_DEFER 0x0002U
+#define DNS_VALIDATOR_NOCDFLAG 0x0004U
+#define DNS_VALIDATOR_NONTA 0x0008U /*% Ignore NTA table */
+
+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 an appropriate 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..0552e81
--- /dev/null
+++ b/lib/dns/include/dns/version.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file dns/version.h */
+
+#ifndef DNS_VERSION_H
+#define DNS_VERSION_H 1
+
+#include <isc/platform.h>
+
+LIBDNS_EXTERNAL_DATA extern const char dns_version[];
+LIBDNS_EXTERNAL_DATA extern const char dns_major[];
+LIBDNS_EXTERNAL_DATA extern const char dns_mapapi[];
+
+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;
+
+#endif /* DNS_VERSION_H */
diff --git a/lib/dns/include/dns/view.h b/lib/dns/include/dns/view.h
new file mode 100644
index 0000000..8e21298
--- /dev/null
+++ b/lib/dns/include/dns/view.h
@@ -0,0 +1,1353 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#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 <inttypes.h>
+#include <stdbool.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/catz.h>
+#include <dns/clientinfo.h>
+#include <dns/dnstap.h>
+#include <dns/fixedname.h>
+#include <dns/rrl.h>
+#include <dns/rdatastruct.h>
+#include <dns/rpz.h>
+#include <dns/types.h>
+#include <dns/zt.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_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;
+
+ /*
+ * security roots and negative trust anchors.
+ * internal use only; access via * dns_view_getsecroots()
+ */
+ dns_keytable_t * secroots_priv;
+ dns_ntatable_t * ntatable_priv;
+
+ isc_mutex_t lock;
+ bool frozen;
+ isc_task_t * task;
+ isc_event_t resevent;
+ isc_event_t adbevent;
+ isc_event_t reqevent;
+ isc_stats_t * adbstats;
+ isc_stats_t * resstats;
+ dns_stats_t * resquerystats;
+ bool cacheshared;
+
+ /* Configurable data. */
+ dns_tsig_keyring_t * statickeys;
+ dns_tsig_keyring_t * dynamickeys;
+ dns_peerlist_t * peers;
+ dns_order_t * order;
+ dns_fwdtable_t * fwdtable;
+ bool recursion;
+ bool auth_nxdomain;
+ bool additionalfromcache;
+ bool additionalfromauth;
+ bool minimal_any;
+ dns_minimaltype_t minimalresponses;
+ bool enablednssec;
+ bool enablevalidation;
+ bool acceptexpired;
+ bool requireservercookie;
+ bool trust_anchor_telemetry;
+ bool root_key_sentinel;
+ dns_transfer_format_t transfer_format;
+ dns_acl_t * cacheacl;
+ dns_acl_t * cacheonacl;
+ 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;
+ dns_acl_t * denyansweracl;
+ dns_acl_t * nocasecompress;
+ bool msgcompression;
+ dns_rbt_t * answeracl_exclude;
+ dns_rbt_t * denyanswernames;
+ dns_rbt_t * answernames_exclude;
+ dns_rrl_t * rrl;
+ bool provideixfr;
+ bool requestnsid;
+ bool sendcookie;
+ dns_ttl_t maxcachettl;
+ dns_ttl_t maxncachettl;
+ uint32_t nta_lifetime;
+ uint32_t nta_recheck;
+ char *nta_file;
+ dns_ttl_t prefetch_trigger;
+ dns_ttl_t prefetch_eligible;
+ in_port_t dstport;
+ dns_aclenv_t aclenv;
+ dns_rdatatype_t preferred_glue;
+ bool flush;
+ dns_namelist_t * delonly;
+ bool rootdelonly;
+ dns_namelist_t * rootexclude;
+ bool checknames;
+ dns_name_t * dlv;
+ dns_fixedname_t dlv_fixed;
+ uint16_t maxudp;
+ uint16_t nocookieudp;
+ unsigned int maxbits;
+ dns_aaaa_t v4_aaaa;
+ dns_aaaa_t v6_aaaa;
+ dns_acl_t * aaaa_acl;
+ dns_dns64list_t dns64;
+ unsigned int dns64cnt;
+ dns_rpz_zones_t *rpzs;
+ dns_catz_zones_t *catzs;
+ dns_dlzdblist_t dlz_searched;
+ dns_dlzdblist_t dlz_unsearched;
+ uint32_t fail_ttl;
+ dns_badcache_t *failcache;
+
+ /*
+ * Configurable data for server use only,
+ * locked by server configuration lock.
+ */
+ dns_acl_t * matchclients;
+ dns_acl_t * matchdestinations;
+ bool 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;
+ dns_viewlist_t * viewlist;
+
+ dns_zone_t * managed_keys;
+ dns_zone_t * redirect;
+ dns_name_t * redirectzone; /* points to
+ * redirectfixed
+ * when valid */
+ dns_fixedname_t redirectfixed;
+
+ /*
+ * File and configuration data for zones added at runtime
+ * (only used in BIND9).
+ *
+ * XXX: This should be a pointer to an opaque type that
+ * named implements.
+ */
+ char * new_zone_file;
+ char * new_zone_db;
+ void * new_zone_dbenv;
+ uint64_t new_zone_mapsize;
+ void * new_zone_config;
+ void (*cfg_destroy)(void **);
+ isc_mutex_t new_zone_lock;
+
+ unsigned char secret[32]; /* Client secret */
+ unsigned int v6bias;
+
+ dns_dtenv_t *dtenv; /* Dnstap environment */
+ dns_dtmsgtype_t dttypes; /* Dnstap message types
+ to log */
+};
+
+#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
+
+#ifdef HAVE_LMDB
+#include <lmdb.h>
+/*
+ * MDB_NOTLS is used to prevent problems after configuration is reloaded, due
+ * to the way LMDB's use of thread-local storage (TLS) interacts with the BIND9
+ * thread model.
+ */
+#define DNS_LMDB_COMMON_FLAGS (MDB_CREATE | MDB_NOSUBDIR | MDB_NOTLS)
+#ifndef __OpenBSD__
+#define DNS_LMDB_FLAGS (DNS_LMDB_COMMON_FLAGS)
+#else /* __OpenBSD__ */
+/*
+ * OpenBSD does not have a unified buffer cache, which requires both reads and
+ * writes to be performed using mmap().
+ */
+#define DNS_LMDB_FLAGS (DNS_LMDB_COMMON_FLAGS | MDB_WRITEMAP)
+#endif /* __OpenBSD__ */
+#endif /* HAVE_LMDB */
+
+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
+ * uncommitted 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_createzonetable(dns_view_t *view);
+/*%<
+ * Create a zonetable for the view.
+ *
+ * Requires:
+ *
+ *\li 'view' is a valid, unfrozen view.
+ *
+ *\li 'view' does not have a zonetable already.
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS
+ *
+ *\li Any error that dns_zt_create() can return.
+ */
+
+isc_result_t
+dns_view_createresolver(dns_view_t *view,
+ isc_taskmgr_t *taskmgr,
+ unsigned int ntasks, unsigned int ndisp,
+ 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);
+void
+dns_view_setcache2(dns_view_t *view, dns_cache_t *cache, bool shared);
+/*%<
+ * Set the view's cache database. If 'shared' is true, this means the cache
+ * is created by another view and is shared with that view. dns_view_setcache()
+ * is a backward compatible version equivalent to setcache2(..., false).
+ *
+ * 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);
+void
+dns_view_setdynamickeyring(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_getdynamickeyring(dns_view_t *view, dns_tsig_keyring_t **ringp);
+/*%<
+ * Return the views dynamic keys.
+ *
+ * \li 'view' is a valid, unfrozen view.
+ * \li 'ringp' != NULL && ringp == NULL.
+ */
+
+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 listening
+ * 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. No changes can be made to view configuration while frozen.
+ *
+ * Requires:
+ *
+ *\li 'view' is a valid, unfrozen view.
+ *
+ * Ensures:
+ *
+ *\li 'view' is frozen.
+ */
+
+void
+dns_view_thaw(dns_view_t *view);
+/*%<
+ * Thaw view. This allows zones to be added or removed at runtime. This is
+ * NOT thread-safe; the caller MUST have run isc_task_exclusive() prior to
+ * thawing the view.
+ *
+ * Requires:
+ *
+ *\li 'view' is a valid, frozen view.
+ *
+ * Ensures:
+ *
+ *\li 'view' is no longer 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, bool 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
+dns_view_find2(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type,
+ isc_stdtime_t now, unsigned int options,
+ bool use_hints, bool use_static_stub,
+ 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'.
+ * In general, this function first searches view's zone and cache DBs for the
+ * best match data against 'name'. If nothing found there, and if 'use_hints'
+ * is true, the view's hint DB (if configured) is searched.
+ * If the view is configured with a static-stub zone which gives the longest
+ * match for 'name' among the zones, however, the cache DB is not consulted
+ * unless 'use_static_stub' is false (see below about this argument).
+ *
+ * dns_view_find() is a backward compatible version equivalent to
+ * dns_view_find2() with use_static_stub argument being false.
+ *
+ * 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 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 'use_static_stub' is false and the longest match zone for 'name'
+ * is a static-stub zone, it's ignored and the cache and/or hints will be
+ * searched. In the majority of the cases this argument should be
+ * false. The only known usage of this argument being true is
+ * if this search is for a "bailiwick" glue A or AAAA RRset that may
+ * best match a static-stub zone. Consider the following example:
+ * this view is configured with a static-stub zone "example.com",
+ * and an attempt of recursive resolution needs to send a query for the
+ * zone. In this case it's quite likely that the resolver is trying to
+ * find A/AAAA RRs for the apex name "example.com". And, to honor the
+ * static-stub configuration it needs to return the glue RRs in the
+ * static-stub zone even if that exact RRs coming from the authoritative
+ * zone has been cached.
+ * In other general cases, the requested data is better to be
+ * authoritative, either locally configured or retrieved from an external
+ * server, and the data in the static-stub zone should better be ignored.
+ *
+ *\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,
+ bool 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 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,
+ bool 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,
+ bool use_hints, bool 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 true, and the view has a hints database, then
+ * it will be searched last.
+ *
+ *\li If 'use_cache' is 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, bool 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.
+ *\li #ISC_R_MULTIPLE Multiple zones with the same name were 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, bool stop);
+
+isc_result_t
+dns_view_loadnew(dns_view_t *view, bool stop);
+
+isc_result_t
+dns_view_asyncload(dns_view_t *view, dns_zt_allloaded_t callback, void *arg);
+
+isc_result_t
+dns_view_asyncload2(dns_view_t *view, dns_zt_allloaded_t callback, void *arg,
+ bool newonly);
+/*%<
+ * 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.
+ *
+ * dns_view_asyncload() loads zones asynchronously. When all zones
+ * in the view have finished loading, 'callback' is called with argument
+ * 'arg' to inform the caller.
+ *
+ * If 'stop' is true, stop on the first error and return it.
+ * If 'stop' is false (or we are loading asynchronously), 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.
+ *
+ * Requires:
+ *\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.
+ *
+ * Requires:
+ * 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);
+isc_result_t
+dns_view_flushcache2(dns_view_t *view, bool fixuponly);
+/*%<
+ * Flush the view's cache (and ADB). If 'fixuponly' is true, it only updates
+ * the internal reference to the cache DB with omitting actual flush operation.
+ * 'fixuponly' is intended to be used for a view that shares a cache with
+ * a different view. dns_view_flushcache() is a backward compatible version
+ * that always sets fixuponly to false.
+ *
+ * Requires:
+ * 'view' is valid.
+ *
+ * No other tasks are executing.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ */
+
+isc_result_t
+dns_view_flushnode(dns_view_t *view, dns_name_t *name, bool tree);
+/*%<
+ * Flush the given name from the view's cache (and optionally ADB/badcache).
+ *
+ * Flush the given name from the cache, ADB, and bad cache. If 'tree'
+ * is true, also flush all subdomains of 'name'.
+ *
+ * Requires:
+ *\li 'view' is valid.
+ *\li 'name' is valid.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ * other returns are failures.
+ */
+
+isc_result_t
+dns_view_flushname(dns_view_t *view, dns_name_t *name);
+/*%<
+ * Flush the given name from the view's cache, ADB and badcache.
+ * Equivalent to dns_view_flushnode(view, name, false).
+ *
+ *
+ * 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
+ */
+
+bool
+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 #true if the name is the table.
+ *\li #false otherwise.
+ */
+
+void
+dns_view_setrootdelonly(dns_view_t *view, bool value);
+/*%<
+ * Set the root delegation only flag.
+ *
+ * Requires:
+ *\li 'view' is valid.
+ */
+
+bool
+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, bool freeze);
+/*%<
+ * Freeze/thaw updates to master zones.
+ *
+ * Requires:
+ * \li 'view' is valid.
+ */
+
+void
+dns_view_setadbstats(dns_view_t *view, isc_stats_t *stats);
+/*%<
+ * Set a adb statistics set 'stats' for 'view'.
+ *
+ * Requires:
+ * \li 'view' is valid and is not frozen.
+ *
+ *\li stats is a valid statistics supporting adb statistics
+ * (see dns/stats.h).
+ */
+
+void
+dns_view_getadbstats(dns_view_t *view, isc_stats_t **statsp);
+/*%<
+ * Get the adb 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_setresstats(dns_view_t *view, isc_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, isc_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
+ */
+
+bool
+dns_view_iscacheshared(dns_view_t *view);
+/*%<
+ * Check if the view shares the cache created by another view.
+ *
+ * Requires:
+ * \li 'view' is valid.
+ *
+ * Returns:
+ *\li #true if the cache is shared.
+ *\li #false otherwise.
+ */
+
+isc_result_t
+dns_view_initntatable(dns_view_t *view,
+ isc_taskmgr_t *taskmgr, isc_timermgr_t *timermgr);
+/*%<
+ * Initialize the negative trust anchor table for the view.
+ *
+ * Requires:
+ * \li 'view' is valid.
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS
+ *\li Any other result indicates failure
+ */
+
+isc_result_t
+dns_view_getntatable(dns_view_t *view, dns_ntatable_t **ntp);
+/*%<
+ * Get the negative trust anchor table for this view. Returns
+ * ISC_R_NOTFOUND if the table not been initialized for the view.
+ *
+ * '*ntp' is attached on success; the caller is responsible for
+ * detaching it with dns_ntatable_detach().
+ *
+ * Requires:
+ * \li 'view' is valid.
+ * \li 'nta' is not NULL and '*nta' is NULL.
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS
+ *\li ISC_R_NOTFOUND
+ */
+
+isc_result_t
+dns_view_initsecroots(dns_view_t *view, isc_mem_t *mctx);
+/*%<
+ * Initialize security roots for the view, detaching any previously
+ * existing security roots first. (Note that secroots_priv is
+ * NULL until this function is called, so any function using
+ * security roots must check that they have been initialized first.
+ * One way to do this is use dns_view_getsecroots() and check its
+ * return value.)
+ *
+ * Requires:
+ * \li 'view' is valid.
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS
+ *\li Any other result indicates failure
+ */
+
+isc_result_t
+dns_view_getsecroots(dns_view_t *view, dns_keytable_t **ktp);
+/*%<
+ * Get the security roots for this view. Returns ISC_R_NOTFOUND if
+ * the security roots keytable has not been initialized for the view.
+ *
+ * '*ktp' is attached on success; the caller is responsible for
+ * detaching it with dns_keytable_detach().
+ *
+ * Requires:
+ * \li 'view' is valid.
+ * \li 'ktp' is not NULL and '*ktp' is NULL.
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS
+ *\li ISC_R_NOTFOUND
+ */
+
+isc_result_t
+dns_view_issecuredomain(dns_view_t *view, dns_name_t *name,
+ isc_stdtime_t now, bool checknta,
+ bool *secure_domain);
+/*%<
+ * Is 'name' at or beneath a trusted key, and not covered by a valid
+ * negative trust anchor? Put answer in '*secure_domain'.
+ *
+ * If 'checknta' is false, ignore the NTA table in determining
+ * whether this is a secure domain.
+ *
+ * Requires:
+ * \li 'view' is valid.
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS
+ *\li Any other value indicates failure
+ */
+
+bool
+dns_view_ntacovers(dns_view_t *view, isc_stdtime_t now,
+ dns_name_t *name, dns_name_t *anchor);
+/*%<
+ * Is there a current negative trust anchor above 'name' and below 'anchor'?
+ *
+ * Requires:
+ * \li 'view' is valid.
+ *
+ * Returns:
+ *\li ISC_R_TRUE
+ *\li ISC_R_FALSE
+ */
+
+void
+dns_view_untrust(dns_view_t *view, dns_name_t *keyname,
+ dns_rdata_dnskey_t *dnskey, isc_mem_t *mctx);
+/*%<
+ * Remove keys that match 'keyname' and 'dnskey' from the views trust
+ * anchors.
+ *
+ * (NOTE: If the configuration specifies that there should be a
+ * trust anchor at 'keyname', but no keys are left after this
+ * operation, that is an error. We fail closed, inserting a NULL
+ * key so as to prevent validation until a legimitate key has been
+ * provided.)
+ *
+ * Requires:
+ * \li 'view' is valid.
+ * \li 'keyname' is valid.
+ * \li 'mctx' is valid.
+ * \li 'dnskey' is valid.
+ */
+
+isc_result_t
+dns_view_setnewzones(dns_view_t *view, bool allow, void *cfgctx,
+ void (*cfg_destroy)(void **), uint64_t mapsize);
+/*%<
+ * Set whether or not to allow zones to be created or deleted at runtime.
+ *
+ * If 'allow' is true, determines the filename into which new zone
+ * configuration will be written. Preserves the configuration context
+ * (a pointer to which is passed in 'cfgctx') for use when parsing new
+ * zone configuration. 'cfg_destroy' points to a callback routine to
+ * destroy the configuration context when the view is destroyed. (This
+ * roundabout method is used in order to avoid libdns having a dependency
+ * on libisccfg and libbind9.)
+ *
+ * If 'allow' is false, removes any existing references to
+ * configuration context and frees any memory.
+ *
+ * Requires:
+ * \li 'view' is valid.
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS
+ * \li ISC_R_NOSPACE
+ */
+
+void
+dns_view_restorekeyring(dns_view_t *view);
+
+isc_result_t
+dns_view_searchdlz(dns_view_t *view, dns_name_t *name,
+ unsigned int minlabels,
+ dns_clientinfomethods_t *methods,
+ dns_clientinfo_t *clientinfo,
+ dns_db_t **dbp);
+
+/*%<
+ * Search through the DLZ database(s) in view->dlz_searched to find
+ * one that can answer a query for 'name', using the DLZ driver's
+ * findzone method. If successful, '*dbp' is set to point to the
+ * DLZ database.
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS
+ * \li ISC_R_NOTFOUND
+ *
+ * Requires:
+ * \li 'view' is valid.
+ * \li 'name' is not NULL.
+ * \li 'dbp' is not NULL and *dbp is NULL.
+ */
+
+uint32_t
+dns_view_getfailttl(dns_view_t *view);
+/*%<
+ * Get the view's servfail-ttl. zero => no servfail caching.
+ *
+ * Requires:
+ *\li 'view' to be valid.
+ */
+
+void
+dns_view_setfailttl(dns_view_t *view, uint32_t failttl);
+/*%<
+ * Set the view's servfail-ttl. zero => no servfail caching.
+ *
+ * Requires:
+ *\li 'view' to be valid.
+ */
+
+isc_result_t
+dns_view_saventa(dns_view_t *view);
+/*%<
+ * Save NTA for names in this view to a file.
+ *
+ * Requires:
+ *\li 'view' to be valid.
+ */
+
+isc_result_t
+dns_view_loadnta(dns_view_t *view);
+/*%<
+ * Loads NTA for names in this view from a file.
+ *
+ * Requires:
+ *\li 'view' to be valid.
+ */
+
+void
+dns_view_setviewcommit(dns_view_t *view);
+/*%<
+ * Commit dns_zone_setview() calls previously made for all zones in this
+ * view.
+ *
+ * Requires:
+ *\li 'view' to be valid.
+ */
+
+void
+dns_view_setviewrevert(dns_view_t *view);
+/*%<
+ * Revert dns_zone_setview() calls previously made for all zones in this
+ * view.
+ *
+ * Requires:
+ *\li 'view' to be valid.
+ */
+
+
+ISC_LANG_ENDDECLS
+
+#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..123ba56
--- /dev/null
+++ b/lib/dns/include/dns/xfrin.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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);
+
+isc_result_t
+dns_xfrin_create3(dns_zone_t *zone, dns_rdatatype_t xfrtype,
+ isc_sockaddr_t *masteraddr, isc_sockaddr_t *sourceaddr,
+ isc_dscp_t dscp, 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_CANCELED.
+ */
+
+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..c059ce2
--- /dev/null
+++ b/lib/dns/include/dns/zone.h
@@ -0,0 +1,2515 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef DNS_ZONE_H
+#define DNS_ZONE_H 1
+
+/*! \file dns/zone.h */
+
+/***
+ *** Imports
+ ***/
+
+#include <stdio.h>
+#include <stdbool.h>
+
+#include <isc/formatcheck.h>
+#include <isc/lang.h>
+#include <isc/rwlock.h>
+
+#include <dns/catz.h>
+#include <dns/master.h>
+#include <dns/masterdump.h>
+#include <dns/rdatastruct.h>
+#include <dns/rpz.h>
+#include <dns/types.h>
+#include <dns/zt.h>
+
+typedef enum {
+ dns_zone_none,
+ dns_zone_master,
+ dns_zone_slave,
+ dns_zone_stub,
+ dns_zone_staticstub,
+ dns_zone_key,
+ dns_zone_dlz,
+ dns_zone_redirect
+} dns_zonetype_t;
+
+typedef enum {
+ dns_zonestat_none = 0,
+ dns_zonestat_terse,
+ dns_zonestat_full
+} dns_zonestat_level_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 */
+#define DNS_ZONEOPT_SECURETOINSECURE 0x08000000U /*%< dnssec-secure-to-insecure */
+#define DNS_ZONEOPT_DNSKEYKSKONLY 0x10000000U /*%< dnssec-dnskey-kskonly */
+#define DNS_ZONEOPT_CHECKDUPRR 0x20000000U /*%< check-dup-records */
+#define DNS_ZONEOPT_CHECKDUPRRFAIL 0x40000000U /*%< fatal check-dup-records failures */
+#define DNS_ZONEOPT_CHECKSPF 0x80000000U /*%< check SPF records */
+
+/*
+ * The following zone options are shifted left into the
+ * higher-order 32 bits of the options.
+ */
+#define DNS_ZONEOPT2_CHECKTTL 0x00000001U /*%< check max-zone-ttl */
+#define DNS_ZONEOPT2_AUTOEMPTY 0x00000002U /*%< automatic empty zone */
+
+#ifndef NOMINUM_PUBLIC
+/*
+ * Nominum specific options build down.
+ */
+#define DNS_ZONEOPT_NOTIFYFORWARD 0x80000000U /* forward notify to master */
+#endif /* NOMINUM_PUBLIC */
+
+/*
+ * Zone key maintenance options
+ */
+#define DNS_ZONEKEY_ALLOW 0x00000001U /*%< fetch keys on command */
+#define DNS_ZONEKEY_MAINTAIN 0x00000002U /*%< publish/sign on schedule */
+#define DNS_ZONEKEY_CREATE 0x00000004U /*%< make keys when needed */
+#define DNS_ZONEKEY_FULLSIGN 0x00000008U /*%< roll to new keys immediately */
+#define DNS_ZONEKEY_NORESIGN 0x00000010U /*%< no automatic resigning */
+
+#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
+#define DNS_ZONESTATE_AUTOMATIC 5
+
+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_result_t
+dns_zone_getserial2(dns_zone_t *zone, uint32_t *serialp);
+
+uint32_t
+dns_zone_getserial(dns_zone_t *zone);
+/*%<
+ * Returns the current serial number of the zone. On success, the SOA
+ * serial of the zone will be copied into '*serialp'.
+ * dns_zone_getserial() cannot catch failure cases and is deprecated by
+ * dns_zone_getserial2().
+ *
+ * Requires:
+ *\li 'zone' to be a valid zone.
+ *\li 'serialp' to be non NULL
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #DNS_R_NOTLOADED zone DB is not loaded
+ */
+
+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.
+ */
+
+void
+dns_zone_setviewcommit(dns_zone_t *zone);
+/*%<
+ * Commit the previous view saved internally via dns_zone_setview().
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ */
+
+void
+dns_zone_setviewrevert(dns_zone_t *zone);
+/*%<
+ * Revert the most recent dns_zone_setview() on this zone,
+ * restoring the previous view.
+ *
+ * Require:
+ *\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);
+isc_result_t
+dns_zone_setfile3(dns_zone_t *zone, const char *file,
+ dns_masterformat_t format, const dns_master_style_t *style);
+/*%<
+ * 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.
+ *
+ * dns_zone_setfile2() is a backward-compatible form of
+ * dns_zone_setfile3(), which also specifies the style
+ * that should be used if a zone using the 'text'
+ * masterformat is ever dumped.
+ *
+ * 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.
+ */
+
+void
+dns_zone_setmaxrecords(dns_zone_t *zone, uint32_t records);
+/*%<
+ * Sets the maximim number of records permitted in a zone.
+ * 0 implies unlimited.
+ *
+ * Requires:
+ *\li 'zone' to be valid initialised zone.
+ *
+ * Returns:
+ *\li void
+ */
+
+uint32_t
+dns_zone_getmaxrecords(dns_zone_t *zone);
+/*%<
+ * Gets the maximim number of records permitted in a zone.
+ * 0 implies unlimited.
+ *
+ * Requires:
+ *\li 'zone' to be valid initialised zone.
+ *
+ * Returns:
+ *\li uint32_t maxrecords.
+ */
+
+void
+dns_zone_setmaxttl(dns_zone_t *zone, uint32_t maxttl);
+/*%<
+ * Sets the max ttl of the zone.
+ *
+ * Requires:
+ *\li 'zone' to be valid initialised zone.
+ *
+ * Returns:
+ *\li void
+ */
+
+dns_ttl_t
+dns_zone_getmaxttl(dns_zone_t *zone);
+/*%<
+ * Gets the max ttl of the zone.
+ *
+ * Requires:
+ *\li 'zone' to be valid initialised zone.
+ *
+ * Returns:
+ *\li dns_ttl_t maxttl.
+ */
+
+isc_result_t
+dns_zone_load(dns_zone_t *zone);
+
+isc_result_t
+dns_zone_loadnew(dns_zone_t *zone);
+
+isc_result_t
+dns_zone_loadandthaw(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.
+ * dns_zone_loadandthaw() is similar to dns_zone_load() but will
+ * also re-enable DNS UPDATEs when the load completes.
+ *
+ * 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().
+ */
+
+isc_result_t
+dns_zone_asyncload(dns_zone_t *zone, dns_zt_zoneloaded_t done, void *arg);
+
+isc_result_t
+dns_zone_asyncload2(dns_zone_t *zone, dns_zt_zoneloaded_t done, void *arg,
+ bool newonly);
+/*%<
+ * Cause the database to be loaded from its backing store asynchronously.
+ * Other zone maintenance functions are suspended until this is complete.
+ * When finished, 'done' is called to inform the caller, with 'arg' as
+ * its first argument and 'zone' as its second. (Normally, 'arg' is
+ * expected to point to the zone table but is left undefined for testing
+ * purposes.)
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ *
+ * Returns:
+ *\li #ISC_R_ALREADYRUNNING
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_FAILURE
+ *\li #ISC_R_NOMEMORY
+ */
+
+bool
+dns__zone_loadpending(dns_zone_t *zone);
+/*%<
+ * Indicates whether the zone is waiting to be loaded asynchronously.
+ * (Not currently intended for use outside of this module and associated
+ * tests.)
+ */
+
+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, bool value);
+/*%<
+ * Sets ('value' == '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
+ */
+
+void
+dns_zone_setdb(dns_zone_t *zone, dns_db_t *db);
+/*%<
+ * Sets the zone database to 'db'.
+ *
+ * This function is expected to be used to configure a zone with a
+ * database which is not loaded from a file or zone transfer.
+ * It can be used for a general purpose zone, but right now its use
+ * is limited to static-stub zones to avoid possible undiscovered
+ * problems in the general cases.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone of static-stub.
+ *\li zone doesn't have a database.
+ */
+
+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 uncommitted 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);
+isc_result_t
+dns_zone_dumptostream3(dns_zone_t *zone, FILE *fd, dns_masterformat_t format,
+ const dns_master_style_t *style,
+ const uint32_t rawversion);
+/*%<
+ * 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.
+ *
+ * dns_zone_dumptostream2() is a backward-compatible form of
+ * dns_zone_dumptostream3(), which always uses the current
+ * default raw file format version.
+ *
+ * Note that dns_zone_dumptostream3() 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 maintenance 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,
+ uint32_t count);
+isc_result_t
+dns_zone_setmasterswithkeys(dns_zone_t *zone,
+ const isc_sockaddr_t *masters,
+ dns_name_t **keynames,
+ 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,
+ uint32_t count);
+isc_result_t
+dns_zone_setalsonotifywithkeys(dns_zone_t *zone, const isc_sockaddr_t *notify,
+ dns_name_t **keynames, uint32_t count);
+isc_result_t
+dns_zone_setalsonotifydscpkeys(dns_zone_t *zone, const isc_sockaddr_t *notify,
+ const isc_dscp_t *dscps, dns_name_t **keynames,
+ uint32_t count);
+/*%<
+ * Set the list of additional servers to be notified when
+ * a zone changes. To clear the list use 'count = 0'.
+ *
+ * dns_zone_alsonotifywithkeys() allows each notify address to
+ * be associated with a TSIG key.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ *\li 'notify' to be non-NULL if count != 0.
+ *\li 'count' to be the number of notifiees.
+ *
+ * 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,
+ bool value);
+void
+dns_zone_setoption2(dns_zone_t *zone, unsigned int option,
+ bool value);
+/*%<
+ * Set the given options on ('value' == true) or off
+ * ('value' == #false).
+ *
+ * dns_zone_setoption2() has been introduced because the number
+ * of options needed now exceeds the 32 bits in the zone->options
+ * field; it should be used set options with names beginning
+ * with DNS_ZONEOPT2_.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ */
+
+unsigned int
+dns_zone_getoptions(dns_zone_t *zone);
+unsigned int
+dns_zone_getoptions2(dns_zone_t *zone);
+/*%<
+ * Returns the current zone options.
+ *
+ * Callers should be aware there is now more than one set of zone
+ * options. dns_zone_getoptions2() has been introduced because the
+ * number of options needed now exceeds the 32 bits in the
+ * zone->options field. It returns the options whose names begin
+ * with DNS_ZONEOPT2_.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ */
+
+void
+dns_zone_setkeyopt(dns_zone_t *zone, unsigned int option, bool value);
+/*%<
+ * Set key options on ('value' == true) or off ('value' ==
+ * #false).
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ */
+
+unsigned int
+dns_zone_getkeyopts(dns_zone_t *zone);
+/*%<
+ * Returns the current zone key options.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ */
+
+void
+dns_zone_setminrefreshtime(dns_zone_t *zone, uint32_t val);
+/*%<
+ * Set the minimum refresh time.
+ *
+ * Requires:
+ *\li 'zone' is valid.
+ *\li val > 0.
+ */
+
+void
+dns_zone_setmaxrefreshtime(dns_zone_t *zone, uint32_t val);
+/*%<
+ * Set the maximum refresh time.
+ *
+ * Requires:
+ *\li 'zone' is valid.
+ *\li val > 0.
+ */
+
+void
+dns_zone_setminretrytime(dns_zone_t *zone, uint32_t val);
+/*%<
+ * Set the minimum retry time.
+ *
+ * Requires:
+ *\li 'zone' is valid.
+ *\li val > 0.
+ */
+
+void
+dns_zone_setmaxretrytime(dns_zone_t *zone, 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_setxfrsource4dscp(dns_zone_t *zone, isc_dscp_t dscp);
+isc_result_t
+dns_zone_setaltxfrsource4dscp(dns_zone_t *zone, isc_dscp_t dscp);
+/*%<
+ * Set the DSCP value associated with the transfer/alt-transfer source.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ */
+
+isc_dscp_t
+dns_zone_getxfrsource4dscp(dns_zone_t *zone);
+isc_dscp_t
+dns_zone_getaltxfrsource4dscp(dns_zone_t *zone);
+/*%/
+ * Get the DSCP value associated with the transfer/alt-transfer source.
+ *
+ * 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_dscp_t
+dns_zone_getxfrsource6dscp(dns_zone_t *zone);
+isc_dscp_t
+dns_zone_getaltxfrsource6dscp(dns_zone_t *zone);
+/*%/
+ * Get the DSCP value associated with the transfer/alt-transfer source.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ */
+
+isc_result_t
+dns_zone_setxfrsource6dscp(dns_zone_t *zone, isc_dscp_t dscp);
+isc_result_t
+dns_zone_setaltxfrsource6dscp(dns_zone_t *zone, isc_dscp_t dscp);
+/*%<
+ * Set the DSCP value associated with the transfer/alt-transfer source.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ */
+
+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_dscp_t
+dns_zone_getnotifysrc4dscp(dns_zone_t *zone);
+/*%/
+ * Get the DSCP value associated with the IPv4 notify source.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ */
+
+isc_result_t
+dns_zone_setnotifysrc4dscp(dns_zone_t *zone, isc_dscp_t dscp);
+/*%<
+ * Set the DSCP value associated with the IPv4 notify source.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ */
+
+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.
+ */
+
+isc_dscp_t
+dns_zone_getnotifysrc6dscp(dns_zone_t *zone);
+/*%/
+ * Get the DSCP value associated with the IPv6 notify source.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ */
+
+isc_result_t
+dns_zone_setnotifysrc6dscp(dns_zone_t *zone, isc_dscp_t dscp);
+/*%<
+ * Set the DSCP value associated with the IPv6 notify source.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ */
+
+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.
+ */
+
+bool
+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, bool 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.
+ */
+
+bool
+dns_zone_getzeronosoattl(dns_zone_t *zone);
+/*%<
+ * Return zero-no-soa-ttl status.
+ */
+
+void
+dns_zone_setzeronosoattl(dns_zone_t *zone, bool 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, int32_t size);
+/*%<
+ * Sets the journal size for the zone.
+ *
+ * Requires:
+ *\li 'zone' to be a valid zone.
+ */
+
+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);
+isc_result_t
+dns_zone_notifyreceive2(dns_zone_t *zone, isc_sockaddr_t *from,
+ isc_sockaddr_t *to, dns_message_t *msg);
+/*%<
+ * Tell the zone that it has received a NOTIFY message from another
+ * server. This may cause some zone maintenance activity to occur.
+ *
+ * Requires:
+ *\li 'zone' to be a valid zone.
+ *\li '*from' to contain the address of the server from which 'msg'
+ * was received.
+ *\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, 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.
+ */
+
+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, 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.
+ */
+
+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 *myjournal);
+/*%<
+ * Sets the filename used for journaling updates / IXFR transfers.
+ * The default journal name is set by dns_zone_setfile() to be
+ * "file.jnl". If 'myjournal' 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, bool dump);
+/*%<
+ * Replace the database of "zone" with a new database "db".
+ *
+ * If "dump" is 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 false to avoid a redundant redump of the data just
+ * loaded. Otherwise, it should be set to 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 consistency checks:
+ * * a single SOA must exist
+ * * some NS records must exist.
+ * Others
+ */
+
+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, 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.
+ */
+
+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, 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, 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.
+ */
+
+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, 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.
+ */
+
+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 exhausted 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. Note: the zone manager will not be able to
+ * manage any zones until dns_zonemgr_setsize() has been run.
+ *
+ * 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_setsize(dns_zonemgr_t *zmgr, int num_zones);
+/*%<
+ * Set the size of the zone manager task pool. This must be run
+ * before zmgr can be used for managing zones. Currently, it can only
+ * be run once; the task pool cannot be resized.
+ *
+ * Requires:
+ *\li zmgr is a valid zone manager.
+ *\li zmgr->zonetasks has been initialized.
+ */
+
+isc_result_t
+dns_zonemgr_createzone(dns_zonemgr_t *zmgr, dns_zone_t **zonep);
+/*%<
+ * Allocate a new zone using a memory context from the
+ * zone manager's memory context pool.
+ *
+ * Require:
+ *\li 'zmgr' to be a valid zone manager.
+ *\li 'zonep' != NULL and '*zonep' == NULL.
+ */
+
+
+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 loaded zones managed by 'zmgr'
+ * to take place at the system's earliest convenience.
+ */
+
+void
+dns__zonemgr_run(isc_task_t *task, isc_event_t *event);
+/*%<
+ * Event handler to call dns_zonemgr_forcemaint(); used to start
+ * zone operations from a unit test. Not intended for use outside
+ * libdns or related tests.
+ */
+
+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, 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.
+ */
+
+uint32_t
+dns_zonemgr_getttransfersin(dns_zonemgr_t *zmgr);
+/*%<
+ * Return 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, uint32_t value);
+/*%<
+ * Set the number of zone transfers allowed per nameserver.
+ *
+ * Requires:
+ *\li 'zmgr' to be a valid zone manager
+ */
+
+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, 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.
+ */
+
+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_setnotifyrate(dns_zonemgr_t *zmgr, unsigned int value);
+/*%<
+ * Set the number of NOTIFY requests sent per second.
+ *
+ * Requires:
+ *\li 'zmgr' to be a valid zone manager
+ */
+
+void
+dns_zonemgr_setstartupnotifyrate(dns_zonemgr_t *zmgr, unsigned int value);
+/*%<
+ * Set the number of startup NOTIFY requests sent per second.
+ *
+ * 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_getnotifyrate(dns_zonemgr_t *zmgr);
+/*%<
+ * Return the number of NOTIFY requests sent per second.
+ *
+ * Requires:
+ *\li 'zmgr' to be a valid zone manager.
+ */
+
+unsigned int
+dns_zonemgr_getstartupnotifyrate(dns_zonemgr_t *zmgr);
+/*%<
+ * Return the number of startup NOTIFY requests 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.
+ */
+
+bool
+dns_zonemgr_unreachable(dns_zonemgr_t *zmgr, isc_sockaddr_t *remote,
+ isc_sockaddr_t *local, isc_time_t *now);
+/*%<
+ * Returns true if the given local/remote address pair
+ * is found in the zone maanger's 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.
+ *\li 'now' != NULL
+ */
+
+void
+dns_zonemgr_unreachabledel(dns_zonemgr_t *zmgr, isc_sockaddr_t *remote,
+ isc_sockaddr_t *local);
+/*%<
+ * Remove the pair of addresses from 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.
+ */
+
+bool
+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, bool on);
+/*%<
+ * This function is obsoleted by dns_zone_setrequeststats().
+ */
+
+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, isc_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, isc_stats_t *stats);
+
+void
+dns_zone_setrcvquerystats(dns_zone_t *zone, dns_stats_t *stats);
+/*%<
+ * Set additional statistics sets to zone. These are attached to the zone
+ * but are 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.
+ */
+
+isc_stats_t *
+dns_zone_getrequeststats(dns_zone_t *zone);
+
+dns_stats_t *
+dns_zone_getrcvquerystats(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_logv(dns_zone_t *zone, isc_logcategory_t *category, int level,
+ const char *prefix, const char *msg, va_list ap);
+/*%<
+ * Log the message 'msg...' at 'level' using log category 'category', including
+ * text that identifies the message as applying to 'zone'. If the (optional)
+ * 'prefix' is not NULL, it will be placed at the start of the entire log line.
+ */
+
+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.
+ */
+
+void
+dns_zone_nameonly(dns_zone_t *zone, char *buf, size_t len);
+/*%<
+ * Return the name of the zone only.
+ *
+ * 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 TARGET 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 'checkns'.
+ * 'checkns' will be called if the NS TARGET is not within the zone.
+ *
+ * Require:
+ * 'zone' to be a valid zone.
+ */
+
+void
+dns_zone_setnotifydelay(dns_zone_t *zone, uint32_t delay);
+/*%<
+ * Set the minimum delay between sets of notify messages.
+ *
+ * Requires:
+ * 'zone' to be valid.
+ */
+
+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.
+ *
+ * bool
+ * 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 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, uint32_t nodes);
+/*%<
+ * Set the number of nodes that will be checked per quantum.
+ */
+
+void
+dns_zone_setsignatures(dns_zone_t *zone, uint32_t signatures);
+/*%<
+ * Set the number of signatures that will be generated per quantum.
+ */
+
+uint32_t
+dns_zone_getsignatures(dns_zone_t *zone);
+/*%<
+ * Get the number of signatures that will be generated per quantum.
+ */
+
+isc_result_t
+dns_zone_signwithkey(dns_zone_t *zone, dns_secalg_t algorithm,
+ uint16_t keyid, bool deleteit);
+/*%<
+ * 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.
+ */
+
+void
+dns_zone_rekey(dns_zone_t *zone, bool fullsign);
+/*%<
+ * Update the zone's DNSKEY set from the key repository.
+ *
+ * If 'fullsign' is true, trigger an immediate full signing of
+ * the zone with the new key. Otherwise, if there are no keys or
+ * if the new keys are for algorithms that have already signed the
+ * zone, then the zone can be re-signed incrementally.
+ */
+
+isc_result_t
+dns_zone_nscheck(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version,
+ unsigned int *errors);
+/*%
+ * Check if the name servers for the zone are sane (have address, don't
+ * refer to CNAMEs/DNAMEs. The number of constiancy errors detected in
+ * returned in '*errors'
+ *
+ * Requires:
+ * \li 'zone' to be valid.
+ * \li 'db' to be valid.
+ * \li 'version' to be valid or NULL.
+ * \li 'errors' to be non NULL.
+ *
+ * Returns:
+ * ISC_R_SUCCESS if there were no errors examining the zone contents.
+ */
+
+isc_result_t
+dns_zone_cdscheck(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version);
+/*%
+ * Check if CSD, CDNSKEY and DNSKEY are consistent.
+ *
+ * Requires:
+ * \li 'zone' to be valid.
+ * \li 'db' to be valid.
+ * \li 'version' to be valid or NULL.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #DNS_R_BADCDS
+ *\li #DNS_R_BADCDNSKEY
+ * Others
+ */
+
+void
+dns_zone_setadded(dns_zone_t *zone, bool added);
+/*%
+ * Sets the value of zone->added, which should be true for
+ * zones that were originally added by "rndc addzone".
+ *
+ * Requires:
+ * \li 'zone' to be valid.
+ */
+
+bool
+dns_zone_getadded(dns_zone_t *zone);
+/*%
+ * Returns true if the zone was originally added at runtime
+ * using "rndc addzone".
+ *
+ * Requires:
+ * \li 'zone' to be valid.
+ */
+
+void
+dns_zone_setautomatic(dns_zone_t *zone, bool automatic);
+/*%
+ * Sets the value of zone->automatic, which should be true for
+ * zones that were automatically added by named.
+ *
+ * Requires:
+ * \li 'zone' to be valid.
+ */
+
+bool
+dns_zone_getautomatic(dns_zone_t *zone);
+/*%
+ * Returns true if the zone was added automatically by named.
+ *
+ * Requires:
+ * \li 'zone' to be valid.
+ */
+
+isc_result_t
+dns_zone_dlzpostload(dns_zone_t *zone, dns_db_t *db);
+/*%
+ * Load the origin names for a writeable DLZ database.
+ */
+
+bool
+dns_zone_isdynamic(dns_zone_t *zone, bool ignore_freeze);
+/*%
+ * 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, key 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; }".
+ *
+ * If 'ignore_freeze' is true, then the zone which has had updates disabled
+ * will still report itself to be dynamic.
+ *
+ * Requires:
+ * \li 'zone' to be valid.
+ */
+
+isc_result_t
+dns_zone_setrefreshkeyinterval(dns_zone_t *zone, uint32_t interval);
+/*%
+ * Sets the frequency, in minutes, with which the key repository will be
+ * checked to see if the keys for this zone have been updated. Any value
+ * higher than 1440 minutes (24 hours) will be silently reduced. A
+ * value of zero will return an out-of-range error.
+ *
+ * Requires:
+ * \li 'zone' to be valid.
+ */
+
+bool
+dns_zone_getrequestexpire(dns_zone_t *zone);
+/*%
+ * Returns the true/false value of the request-expire option in the zone.
+ *
+ * Requires:
+ * \li 'zone' to be valid.
+ */
+
+void
+dns_zone_setrequestexpire(dns_zone_t *zone, bool flag);
+/*%
+ * Sets the request-expire option for the zone. Either true or false. The
+ * default value is determined by the setting of this option in the view.
+ *
+ * Requires:
+ * \li 'zone' to be valid.
+ */
+
+
+bool
+dns_zone_getrequestixfr(dns_zone_t *zone);
+/*%
+ * Returns the true/false value of the request-ixfr option in the zone.
+ *
+ * Requires:
+ * \li 'zone' to be valid.
+ */
+
+void
+dns_zone_setrequestixfr(dns_zone_t *zone, bool flag);
+/*%
+ * Sets the request-ixfr option for the zone. Either true or false. The
+ * default value is determined by the setting of this option in the view.
+ *
+ * Requires:
+ * \li 'zone' to be valid.
+ */
+
+void
+dns_zone_setserialupdatemethod(dns_zone_t *zone, dns_updatemethod_t method);
+/*%
+ * Sets the update method to use when incrementing the zone serial number
+ * due to a DDNS update. Valid options are dns_updatemethod_increment
+ * and dns_updatemethod_unixtime.
+ *
+ * Requires:
+ * \li 'zone' to be valid.
+ */
+
+dns_updatemethod_t
+dns_zone_getserialupdatemethod(dns_zone_t *zone);
+/*%
+ * Returns the update method to be used when incrementing the zone serial
+ * number due to a DDNS update.
+ *
+ * Requires:
+ * \li 'zone' to be valid.
+ */
+
+isc_result_t
+dns_zone_link(dns_zone_t *zone, dns_zone_t *raw);
+
+void
+dns_zone_getraw(dns_zone_t *zone, dns_zone_t **raw);
+
+isc_result_t
+dns_zone_keydone(dns_zone_t *zone, const char *data);
+
+isc_result_t
+dns_zone_setnsec3param(dns_zone_t *zone, uint8_t hash, uint8_t flags,
+ uint16_t iter, uint8_t saltlen,
+ unsigned char *salt, bool replace);
+/*%
+ * Set the NSEC3 parameters for the zone.
+ *
+ * If 'replace' is true, then the existing NSEC3 chain, if any, will
+ * be replaced with the new one. If 'hash' is zero, then the replacement
+ * chain will be NSEC rather than NSEC3.
+ *
+ * Requires:
+ * \li 'zone' to be valid.
+ */
+
+void
+dns_zone_setrawdata(dns_zone_t *zone, dns_masterrawheader_t *header);
+/*%
+ * Set the data to be included in the header when the zone is dumped in
+ * binary format.
+ */
+
+isc_result_t
+dns_zone_synckeyzone(dns_zone_t *zone);
+/*%
+ * Force the managed key zone to synchronize, and start the key
+ * maintenance timer.
+ */
+
+isc_result_t
+dns_zone_getloadtime(dns_zone_t *zone, isc_time_t *loadtime);
+/*%
+ * Return the time when the zone was last loaded.
+ */
+
+isc_result_t
+dns_zone_getrefreshtime(dns_zone_t *zone, isc_time_t *refreshtime);
+/*%
+ * Return the time when the (slave) zone will need to be refreshed.
+ */
+
+isc_result_t
+dns_zone_getexpiretime(dns_zone_t *zone, isc_time_t *expiretime);
+/*%
+ * Return the time when the (slave) zone will expire.
+ */
+
+isc_result_t
+dns_zone_getrefreshkeytime(dns_zone_t *zone, isc_time_t *refreshkeytime);
+/*%
+ * Return the time of the next scheduled DNSSEC key event.
+ */
+
+unsigned int
+dns_zone_getincludes(dns_zone_t *zone, char ***includesp);
+/*%
+ * Return the number include files that were encountered
+ * during load. If the number is greater than zero, 'includesp'
+ * will point to an array containing the filenames.
+ *
+ * The array and its contents need to be freed using isc_mem_free.
+ */
+
+isc_result_t
+dns_zone_rpz_enable(dns_zone_t *zone, dns_rpz_zones_t *rpzs,
+ dns_rpz_num_t rpz_num);
+/*%
+ * Set the response policy associated with a zone.
+ */
+
+void
+dns_zone_rpz_enable_db(dns_zone_t *zone, dns_db_t *db);
+/*%
+ * If a zone is a response policy zone, mark its new database.
+ */
+
+dns_rpz_num_t
+dns_zone_get_rpz_num(dns_zone_t *zone);
+
+void
+dns_zone_catz_enable(dns_zone_t *zone, dns_catz_zones_t *catzs);
+/*%<
+ * Enable zone as catalog zone.
+ *
+ * Requires:
+ *
+ * \li 'zone' is a valid zone object
+ * \li 'catzs' is not NULL
+ * \li prior to calling, zone->catzs is NULL or is equal to 'catzs'
+ */
+
+void
+dns_zone_catz_enable_db(dns_zone_t *zone, dns_db_t *db);
+/*%<
+ * If 'zone' is a catalog zone, then set up a notify-on-update trigger
+ * in its database. (If not a catalog zone, this function has no effect.)
+ *
+ * Requires:
+ *
+ * \li 'zone' is a valid zone object
+ * \li 'db' is not NULL
+ */
+void
+dns_zone_set_parentcatz(dns_zone_t *zone, dns_catz_zone_t *catz);
+/*%<
+ * Set parent catalog zone for this zone
+ *
+ * Requires:
+ *
+ * \li 'zone' is a valid zone object
+ * \li 'catz' is not NULL
+ */
+
+dns_catz_zone_t *
+dns_zone_get_parentcatz(const dns_zone_t *zone);
+/*%<
+ * Get parent catalog zone for this zone
+ *
+ * Requires:
+ *
+ * \li 'zone' is a valid zone object
+ */
+
+
+void
+dns_zone_setstatlevel(dns_zone_t *zone, dns_zonestat_level_t level);
+
+dns_zonestat_level_t
+dns_zone_getstatlevel(dns_zone_t *zone);
+/*%
+ * Set and get the statistics reporting level for the zone;
+ * full, terse, or none.
+ */
+
+isc_result_t
+dns_zone_setserial(dns_zone_t *zone, uint32_t serial);
+/*%
+ * Set the zone's serial to 'serial'.
+ */
+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..01a0369
--- /dev/null
+++ b/lib/dns/include/dns/zonekey.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#ifndef DNS_ZONEKEY_H
+#define DNS_ZONEKEY_H 1
+
+/*! \file dns/zonekey.h */
+
+#include <stdbool.h>
+
+#include <isc/lang.h>
+
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+bool
+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..84ca8ea
--- /dev/null
+++ b/lib/dns/include/dns/zt.h
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#ifndef DNS_ZT_H
+#define DNS_ZT_H 1
+
+/*! \file dns/zt.h */
+
+#include <stdbool.h>
+
+#include <isc/lang.h>
+
+#include <dns/types.h>
+
+#define DNS_ZTFIND_NOEXACT 0x01
+
+ISC_LANG_BEGINDECLS
+
+typedef isc_result_t
+(*dns_zt_allloaded_t)(void *arg);
+/*%<
+ * Method prototype: when all pending zone loads are complete,
+ * the zone table can inform the caller via a callback function with
+ * this signature.
+ */
+
+typedef isc_result_t
+(*dns_zt_zoneloaded_t)(dns_zt_t *zt, dns_zone_t *zone, isc_task_t *task);
+/*%<
+ * Method prototype: when a zone finishes loading, the zt object
+ * can be informed via a callback function with this signature.
+ */
+
+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, const 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, bool stop);
+
+isc_result_t
+dns_zt_loadnew(dns_zt_t *zt, bool stop);
+
+isc_result_t
+dns_zt_asyncload(dns_zt_t *zt, dns_zt_allloaded_t alldone, void *arg);
+
+isc_result_t
+dns_zt_asyncload2(dns_zt_t *zt, dns_zt_allloaded_t alldone, void *arg,
+ bool newonly);
+/*%<
+ * Load all zones in the table. If 'stop' is true,
+ * stop on the first error and return it. If 'stop'
+ * is 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.
+ * dns_zt_asyncload() loads zones asynchronously; when all
+ * zones in the zone table have finished loaded (or failed due
+ * to errors), the caller is informed by calling 'alldone'
+ * with an argument of 'arg'.
+ *
+ * Requires:
+ * \li 'zt' to be valid
+ */
+
+isc_result_t
+dns_zt_freezezones(dns_zt_t *zt, bool 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, bool stop,
+ isc_result_t (*action)(dns_zone_t *, void *), void *uap);
+
+isc_result_t
+dns_zt_apply2(dns_zt_t *zt, bool 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 '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
+ * false and 'sub' is non NULL then the first error (if any)
+ * reported by 'action' is returned in '*sub';
+ * any error code from 'action'.
+ */
+
+bool
+dns_zt_loadspending(dns_zt_t *zt);
+/*%<
+ * Returns true if and only if there are zones still waiting to
+ * be loaded in zone table 'zt'.
+ *
+ * Requires:
+ * \li 'zt' to be valid.
+ */
+
+void
+dns_zt_setviewcommit(dns_zt_t *zt);
+/*%<
+ * Commit dns_zone_setview() calls previously made for all zones in this
+ * zone table.
+ *
+ * Requires:
+ *\li 'view' to be valid.
+ */
+
+void
+dns_zt_setviewrevert(dns_zt_t *zt);
+/*%<
+ * Revert dns_zone_setview() calls previously made for all zones in this
+ * zone table.
+ *
+ * Requires:
+ *\li 'view' to be valid.
+ */
+
+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..04727f7
--- /dev/null
+++ b/lib/dns/include/dst/Makefile.in
@@ -0,0 +1,34 @@
+# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# See the COPYRIGHT file distributed with this work for additional
+# information regarding copyright ownership.
+
+srcdir = @srcdir@
+VPATH = @srcdir@
+top_srcdir = @top_srcdir@
+
+VERSION=@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 || exit 1; \
+ done
+
+uninstall::
+ for i in ${HEADERS}; do \
+ rm -f ${DESTDIR}${includedir}/dst/$$i || exit 1; \
+ done
diff --git a/lib/dns/include/dst/dst.h b/lib/dns/include/dst/dst.h
new file mode 100644
index 0000000..1924e74
--- /dev/null
+++ b/lib/dns/include/dst/dst.h
@@ -0,0 +1,995 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef DST_DST_H
+#define DST_DST_H 1
+
+/*! \file dst/dst.h */
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/lang.h>
+#include <isc/stdtime.h>
+
+#include <dns/types.h>
+#include <dns/log.h>
+#include <dns/name.h>
+#include <dns/secalg.h>
+#include <dns/ds.h>
+#include <dns/dsdigest.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_RSASHA256 8
+#define DST_ALG_RSASHA512 10
+#define DST_ALG_ECCGOST 12
+#define DST_ALG_ECDSA256 13
+#define DST_ALG_ECDSA384 14
+#define DST_ALG_ED25519 15
+#define DST_ALG_ED448 16
+#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_INDIRECT 252
+#define DST_ALG_PRIVATE 254
+#define DST_MAX_ALGS 256
+
+/*% 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
+
+/* Key timing metadata definitions */
+#define DST_TIME_CREATED 0
+#define DST_TIME_PUBLISH 1
+#define DST_TIME_ACTIVATE 2
+#define DST_TIME_REVOKE 3
+#define DST_TIME_INACTIVE 4
+#define DST_TIME_DELETE 5
+#define DST_TIME_DSPUBLISH 6
+#define DST_TIME_SYNCPUBLISH 7
+#define DST_TIME_SYNCDELETE 8
+#define DST_MAX_TIMES 8
+
+/* Numeric metadata definitions */
+#define DST_NUM_PREDECESSOR 0
+#define DST_NUM_SUCCESSOR 1
+#define DST_NUM_MAXTTL 2
+#define DST_NUM_ROLLPERIOD 3
+#define DST_MAX_NUMERIC 3
+
+/*
+ * Current format version number of the private key parser.
+ *
+ * When parsing a key file with the same major number but a higher minor
+ * number, the key parser will ignore any fields it does not recognize.
+ * Thus, DST_MINOR_VERSION should be incremented whenever new
+ * fields are added to the private key file (such as new metadata).
+ *
+ * When rewriting these keys, those fields will be dropped, and the
+ * format version set back to the current one..
+ *
+ * When a key is seen with a higher major number, the key parser will
+ * reject it as invalid. Thus, DST_MAJOR_VERSION should be incremented
+ * and DST_MINOR_VERSION set to zero whenever there is a format change
+ * which is not backward compatible to previous versions of the dst_key
+ * parser, such as change in the syntax of an existing field, the removal
+ * of a currently mandatory field, or a new field added which would
+ * alter the functioning of the key if it were absent.
+ */
+#define DST_MAJOR_VERSION 1
+#define DST_MINOR_VERSION 3
+
+/***
+ *** Functions
+ ***/
+
+isc_result_t
+dst_lib_init(isc_mem_t *mctx, isc_entropy_t *ectx, unsigned int eflags);
+
+isc_result_t
+dst_lib_init2(isc_mem_t *mctx, isc_entropy_t *ectx,
+ const char *engine, 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
+ * \li DST_R_NOENGINE
+ *
+ * Ensures:
+ * \li DST is properly initialized.
+ */
+
+void
+dst_lib_destroy(void);
+/*%<
+ * Releases all resources allocated by DST.
+ */
+
+bool
+dst_algorithm_supported(unsigned int alg);
+/*%<
+ * Checks that a given algorithm is supported by DST.
+ *
+ * Returns:
+ * \li true
+ * \li false
+ */
+
+bool
+dst_ds_digest_supported(unsigned int digest_type);
+/*%<
+ * Checks that a given digest algorithm is supported by DST.
+ *
+ * Returns:
+ * \li true
+ * \li false
+ */
+
+isc_result_t
+dst_context_create(dst_key_t *key, isc_mem_t *mctx, dst_context_t **dctxp);
+
+isc_result_t
+dst_context_create2(dst_key_t *key, isc_mem_t *mctx,
+ isc_logcategory_t *category, dst_context_t **dctxp);
+
+isc_result_t
+dst_context_create3(dst_key_t *key, isc_mem_t *mctx,
+ isc_logcategory_t *category, bool useforsigning,
+ dst_context_t **dctxp);
+
+isc_result_t
+dst_context_create4(dst_key_t *key, isc_mem_t *mctx,
+ isc_logcategory_t *category, bool useforsigning,
+ int maxbits, 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);
+
+isc_result_t
+dst_context_verify2(dst_context_t *dctx, unsigned int maxbits,
+ isc_region_t *sig);
+/*%<
+ * Verifies the signature using the data and key stored in the context.
+ *
+ * 'maxbits' specifies the maximum number of bits permitted in the RSA
+ * exponent.
+ *
+ * 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_getfilename(dns_name_t *name, dns_keytag_t id, unsigned int alg,
+ int type, const char *directory,
+ isc_mem_t *mctx, isc_buffer_t *buf);
+/*%<
+ * Generates a key filename for the name, algorithm, and
+ * id, and places it in the buffer 'buf'. 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 "buf" is not NULL.
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS
+ * \li any other result indicates failure
+ */
+
+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, const char *dirname,
+ 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.
+ *
+ * If 'dirname' is not NULL, and 'filename' is a relative path,
+ * then the file is looked up relative to the given directory.
+ * If 'filename' is an absolute path, 'dirname' is ignored.
+ *
+ * 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, isc_region_t *intoken);
+/*%<
+ * 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.
+ */
+
+#ifdef DST_KEY_INTERNAL
+isc_result_t
+dst_key_buildinternal(dns_name_t *name, unsigned int alg,
+ unsigned int bits, unsigned int flags,
+ unsigned int protocol, dns_rdataclass_t rdclass,
+ void *data, isc_mem_t *mctx, dst_key_t **keyp);
+#endif
+
+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);
+
+isc_result_t
+dst_key_generate2(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,
+ void (*callback)(int));
+
+/*%<
+ * 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.
+ */
+
+bool
+dst_key_compare(const dst_key_t *key1, const dst_key_t *key2);
+/*%<
+ * Compares two DST keys. Returns true if they match, false otherwise.
+ *
+ * Keys ARE NOT considered to match if one of them is the revoked version
+ * of the other.
+ *
+ * Requires:
+ *\li "key1" is a valid key.
+ *\li "key2" is a valid key.
+ *
+ * Returns:
+ *\li true
+ * \li false
+ */
+
+bool
+dst_key_pubcompare(const dst_key_t *key1, const dst_key_t *key2,
+ bool match_revoked_key);
+/*%<
+ * Compares only the public portions of two DST keys. Returns true
+ * if they match, false otherwise. This allows us, for example, to
+ * determine whether a public key found in a zone matches up with a
+ * key pair found on disk.
+ *
+ * If match_revoked_key is TRUE, then keys ARE considered to match if one
+ * of them is the revoked version of the other. Otherwise, they are not.
+ *
+ * Requires:
+ *\li "key1" is a valid key.
+ *\li "key2" is a valid key.
+ *
+ * Returns:
+ *\li true
+ * \li false
+ */
+
+bool
+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 true
+ * \li false
+ */
+
+void
+dst_key_attach(dst_key_t *source, dst_key_t **target);
+/*
+ * Attach to a existing key increasing the reference count.
+ *
+ * Requires:
+ *\li 'source' to be a valid key.
+ *\li 'target' to be non-NULL and '*target' to be NULL.
+ */
+
+void
+dst_key_free(dst_key_t **keyp);
+/*%<
+ * Decrement the key's reference counter and, when it reaches zero,
+ * release all memory associated with the key.
+ *
+ * Requires:
+ *\li "keyp" is not NULL and "*keyp" is a valid key.
+ *\li reference counter greater than zero.
+ *
+ * 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);
+
+uint32_t
+dst_key_flags(const dst_key_t *key);
+
+dns_keytag_t
+dst_key_id(const dst_key_t *key);
+
+dns_keytag_t
+dst_key_rid(const dst_key_t *key);
+
+dns_rdataclass_t
+dst_key_class(const dst_key_t *key);
+
+bool
+dst_key_isprivate(const dst_key_t *key);
+
+bool
+dst_key_iszonekey(const dst_key_t *key);
+
+bool
+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
+ */
+
+uint16_t
+dst_region_computeid(const isc_region_t *source, unsigned int alg);
+uint16_t
+dst_region_computerid(const isc_region_t *source, unsigned int alg);
+/*%<
+ * Computes the (revoked) 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
+ */
+
+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, uint16_t bits);
+/*%<
+ * Set the number of digest bits required (0 == MAX).
+ *
+ * Requires:
+ * "key" is a valid key.
+ */
+
+void
+dst_key_setttl(dst_key_t *key, dns_ttl_t ttl);
+/*%<
+ * Set the default TTL to use when converting the key
+ * to a KEY or DNSKEY RR.
+ *
+ * Requires:
+ * "key" is a valid key.
+ */
+
+dns_ttl_t
+dst_key_getttl(const dst_key_t *key);
+/*%<
+ * Get the default TTL to use when converting the key
+ * to a KEY or DNSKEY RR.
+ *
+ * Requires:
+ * "key" is a valid key.
+ */
+
+isc_result_t
+dst_key_setflags(dst_key_t *key, uint32_t flags);
+/*
+ * Set the key flags, and recompute the key ID.
+ *
+ * Requires:
+ * "key" is a valid key.
+ */
+
+isc_result_t
+dst_key_getnum(const dst_key_t *key, int type, uint32_t *valuep);
+/*%<
+ * Get a member of the numeric metadata array and place it in '*valuep'.
+ *
+ * Requires:
+ * "key" is a valid key.
+ * "type" is no larger than DST_MAX_NUMERIC
+ * "timep" is not null.
+ */
+
+void
+dst_key_setnum(dst_key_t *key, int type, uint32_t value);
+/*%<
+ * Set a member of the numeric metadata array.
+ *
+ * Requires:
+ * "key" is a valid key.
+ * "type" is no larger than DST_MAX_NUMERIC
+ */
+
+void
+dst_key_unsetnum(dst_key_t *key, int type);
+/*%<
+ * Flag a member of the numeric metadata array as "not set".
+ *
+ * Requires:
+ * "key" is a valid key.
+ * "type" is no larger than DST_MAX_NUMERIC
+ */
+
+isc_result_t
+dst_key_gettime(const dst_key_t *key, int type, isc_stdtime_t *timep);
+/*%<
+ * Get a member of the timing metadata array and place it in '*timep'.
+ *
+ * Requires:
+ * "key" is a valid key.
+ * "type" is no larger than DST_MAX_TIMES
+ * "timep" is not null.
+ */
+
+void
+dst_key_settime(dst_key_t *key, int type, isc_stdtime_t when);
+/*%<
+ * Set a member of the timing metadata array.
+ *
+ * Requires:
+ * "key" is a valid key.
+ * "type" is no larger than DST_MAX_TIMES
+ */
+
+void
+dst_key_unsettime(dst_key_t *key, int type);
+/*%<
+ * Flag a member of the timing metadata array as "not set".
+ *
+ * Requires:
+ * "key" is a valid key.
+ * "type" is no larger than DST_MAX_TIMES
+ */
+
+isc_result_t
+dst_key_getprivateformat(const dst_key_t *key, int *majorp, int *minorp);
+/*%<
+ * Get the private key format version number. (If the key does not have
+ * a private key associated with it, the version will be 0.0.) The major
+ * version number is placed in '*majorp', and the minor version number in
+ * '*minorp'.
+ *
+ * Requires:
+ * "key" is a valid key.
+ * "majorp" is not NULL.
+ * "minorp" is not NULL.
+ */
+
+void
+dst_key_setprivateformat(dst_key_t *key, int major, int minor);
+/*%<
+ * Set the private key format version number.
+ *
+ * Requires:
+ * "key" is a valid key.
+ */
+
+#define DST_KEY_FORMATSIZE (DNS_NAME_FORMATSIZE + DNS_SECALG_FORMATSIZE + 7)
+
+void
+dst_key_format(const dst_key_t *key, char *cp, unsigned int size);
+/*%<
+ * Write the uniquely identifying information about the key (name,
+ * algorithm, key ID) into a string 'cp' of size 'size'.
+ */
+
+
+isc_buffer_t *
+dst_key_tkeytoken(const dst_key_t *key);
+/*%<
+ * Return the token from the TKEY request, if any. If this key was
+ * not negotiated via TKEY, return NULL.
+ *
+ * Requires:
+ * "key" is a valid key.
+ */
+
+
+isc_result_t
+dst_key_dump(dst_key_t *key, isc_mem_t *mctx, char **buffer, int *length);
+/*%<
+ * Allocate 'buffer' and dump the key into it in base64 format. The buffer
+ * is not NUL terminated. The length of the buffer is returned in *length.
+ *
+ * 'buffer' needs to be freed using isc_mem_put(mctx, buffer, length);
+ *
+ * Requires:
+ * 'buffer' to be non NULL and *buffer to be NULL.
+ * 'length' to be non NULL and *length to be zero.
+ *
+ * Returns:
+ * ISC_R_SUCCESS
+ * ISC_R_NOMEMORY
+ * ISC_R_NOTIMPLEMENTED
+ * others.
+ */
+
+isc_result_t
+dst_key_restore(dns_name_t *name, unsigned int alg, unsigned int flags,
+ unsigned int protocol, dns_rdataclass_t rdclass,
+ isc_mem_t *mctx, const char *keystr, dst_key_t **keyp);
+
+bool
+dst_key_inactive(const dst_key_t *key);
+/*%<
+ * Determines if the private key is missing due the key being deemed inactive.
+ *
+ * Requires:
+ * 'key' to be valid.
+ */
+
+void
+dst_key_setinactive(dst_key_t *key, bool inactive);
+/*%<
+ * Set key inactive state.
+ *
+ * Requires:
+ * 'key' to be valid.
+ */
+
+void
+dst_key_setexternal(dst_key_t *key, bool value);
+
+bool
+dst_key_isexternal(dst_key_t *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..17a9f54
--- /dev/null
+++ b/lib/dns/include/dst/gssapi.h
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#ifndef DST_GSSAPI_H
+#define DST_GSSAPI_H 1
+
+/*! \file dst/gssapi.h */
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/formatcheck.h>
+#include <isc/lang.h>
+#include <isc/platform.h>
+#include <isc/types.h>
+#include <dns/types.h>
+
+#ifdef GSSAPI
+#ifdef WIN32
+/*
+ * MSVC does not like macros in #include lines.
+ */
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_krb5.h>
+#else
+#include ISC_PLATFORM_GSSAPIHEADER
+#ifdef ISC_PLATFORM_GSSAPI_KRB5_HEADER
+#include ISC_PLATFORM_GSSAPI_KRB5_HEADER
+#endif
+#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, bool 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,
+ isc_mem_t *mctx, char **err_message);
+/*
+ * 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
+ * *err_message optional error message
+ */
+
+isc_result_t
+dst_gssapi_acceptctx(gss_cred_id_t cred,
+ const char *gssapi_keytab,
+ 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
+ * DNS_R_CONTINUE transaction still in progress
+ * 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);
+/*
+ * Logging 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(uint32_t major, 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.
+ */
+
+bool
+dst_gssapi_identitymatchesrealmkrb5(const dns_name_t *signer,
+ const dns_name_t *name,
+ const dns_name_t *realm,
+ bool subdomain);
+/*
+ * Compare a "signer" (in the format of a Kerberos-format Kerberos5
+ * principal: host/example.com@EXAMPLE.COM) to the realm name stored
+ * in "name" (which represents the realm name).
+ *
+ */
+
+bool
+dst_gssapi_identitymatchesrealmms(const dns_name_t *signer,
+ const dns_name_t *name,
+ const dns_name_t *realm,
+ bool subdomain);
+/*
+ * Compare a "signer" (in the format of a Kerberos-format Kerberos5
+ * principal: 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..508439f
--- /dev/null
+++ b/lib/dns/include/dst/lib.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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..0855d80
--- /dev/null
+++ b/lib/dns/include/dst/result.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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_CRYPTOFAILURE (ISC_RESULTCLASS_DST + 1)
+/* compat */
+#define DST_R_OPENSSLFAILURE DST_R_CRYPTOFAILURE
+#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_EXTERNALKEY (ISC_RESULTCLASS_DST + 22)
+
+#define DST_R_NRESULTS 23 /* 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/ipkeylist.c b/lib/dns/ipkeylist.c
new file mode 100644
index 0000000..4f114e5
--- /dev/null
+++ b/lib/dns/ipkeylist.c
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <string.h>
+
+#include <isc/mem.h>
+#include <isc/sockaddr.h>
+#include <isc/util.h>
+
+#include <dns/ipkeylist.h>
+#include <dns/name.h>
+
+void
+dns_ipkeylist_init(dns_ipkeylist_t *ipkl) {
+ ipkl->count = 0;
+ ipkl->allocated = 0;
+ ipkl->addrs = NULL;
+ ipkl->dscps = NULL;
+ ipkl->keys = NULL;
+ ipkl->labels = NULL;
+}
+
+void
+dns_ipkeylist_clear(isc_mem_t *mctx, dns_ipkeylist_t *ipkl) {
+ uint32_t i;
+
+ REQUIRE(ipkl != NULL);
+
+ if (ipkl->allocated == 0)
+ return;
+
+ if (ipkl->addrs != NULL)
+ isc_mem_put(mctx, ipkl->addrs,
+ ipkl->allocated * sizeof(isc_sockaddr_t));
+
+ if (ipkl->dscps != NULL)
+ isc_mem_put(mctx, ipkl->dscps,
+ ipkl->allocated * sizeof(isc_dscp_t));
+
+ if (ipkl->keys != NULL) {
+ for (i = 0; i < ipkl->allocated; i++) {
+ if (ipkl->keys[i] == NULL)
+ continue;
+ if (dns_name_dynamic(ipkl->keys[i]))
+ dns_name_free(ipkl->keys[i], mctx);
+ isc_mem_put(mctx, ipkl->keys[i], sizeof(dns_name_t));
+ }
+ isc_mem_put(mctx, ipkl->keys,
+ ipkl->allocated * sizeof(dns_name_t *));
+ }
+
+ if (ipkl->labels != NULL) {
+ for (i = 0; i < ipkl->allocated; i++) {
+ if (ipkl->labels[i] == NULL)
+ continue;
+ if (dns_name_dynamic(ipkl->labels[i]))
+ dns_name_free(ipkl->labels[i], mctx);
+ isc_mem_put(mctx, ipkl->labels[i], sizeof(dns_name_t));
+ }
+ isc_mem_put(mctx, ipkl->labels,
+ ipkl->allocated * sizeof(dns_name_t *));
+ }
+
+ dns_ipkeylist_init(ipkl);
+}
+
+isc_result_t
+dns_ipkeylist_copy(isc_mem_t *mctx, const dns_ipkeylist_t *src,
+ dns_ipkeylist_t *dst)
+{
+ isc_result_t result = ISC_R_SUCCESS;
+ uint32_t i;
+
+ REQUIRE(dst != NULL);
+ /* dst might be preallocated, we don't care, but it must be empty */
+ REQUIRE(dst->count == 0);
+
+ if (src->count == 0)
+ return (ISC_R_SUCCESS);
+
+ result = dns_ipkeylist_resize(mctx, dst, src->count);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ memmove(dst->addrs, src->addrs, src->count * sizeof(isc_sockaddr_t));
+
+ if (src->dscps != NULL) {
+ memmove(dst->dscps, src->dscps,
+ src->count * sizeof(isc_dscp_t));
+ }
+
+ if (src->keys != NULL) {
+ for (i = 0; i < src->count; i++) {
+ if (src->keys[i] != NULL) {
+ dst->keys[i] = isc_mem_get(mctx,
+ sizeof(dns_name_t));
+ if (dst->keys[i] == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup_keys;
+ }
+ dns_name_init(dst->keys[i], NULL);
+ result = dns_name_dup(src->keys[i], mctx,
+ dst->keys[i]);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_keys;
+ } else {
+ dst->keys[i] = NULL;
+ }
+ }
+ }
+
+ if (src->labels != NULL) {
+ for (i = 0; i < src->count; i++) {
+ if (src->labels[i] != NULL) {
+ dst->labels[i] = isc_mem_get(mctx,
+ sizeof(dns_name_t));
+ if (dst->labels[i] == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup_labels;
+ }
+ dns_name_init(dst->labels[i], NULL);
+ result = dns_name_dup(src->labels[i], mctx,
+ dst->labels[i]);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_labels;
+ } else {
+ dst->labels[i] = NULL;
+ }
+ }
+ }
+ dst->count = src->count;
+ return (ISC_R_SUCCESS);
+
+ cleanup_labels:
+ do {
+ if (dst->labels[i] != NULL) {
+ if (dns_name_dynamic(dst->labels[i]))
+ dns_name_free(dst->labels[i], mctx);
+ isc_mem_put(mctx, dst->labels[i], sizeof(dns_name_t));
+ dst->labels[i] = NULL;
+ }
+ } while (i-- > 0);
+
+ cleanup_keys:
+ do {
+ if (dst->keys[i] != NULL) {
+ if (dns_name_dynamic(dst->keys[i]))
+ dns_name_free(dst->keys[i], mctx);
+ isc_mem_put(mctx, dst->keys[i], sizeof(dns_name_t));
+ dst->keys[i] = NULL;
+ }
+ } while (i-- > 0);
+
+ return (result);
+}
+
+isc_result_t
+dns_ipkeylist_resize(isc_mem_t *mctx, dns_ipkeylist_t *ipkl, unsigned int n) {
+ isc_sockaddr_t *addrs = NULL;
+ isc_dscp_t *dscps = NULL;
+ dns_name_t **keys = NULL;
+ dns_name_t **labels = NULL;
+
+ REQUIRE(ipkl != NULL);
+ REQUIRE(n > ipkl->count);
+
+ if (n <= ipkl->allocated)
+ return (ISC_R_SUCCESS);
+
+ addrs = isc_mem_get(mctx, n * sizeof(isc_sockaddr_t));
+ if (addrs == NULL)
+ goto nomemory;
+ dscps = isc_mem_get(mctx, n * sizeof(isc_dscp_t));
+ if (dscps == NULL)
+ goto nomemory;
+ keys = isc_mem_get(mctx, n * sizeof(dns_name_t *));
+ if (keys == NULL)
+ goto nomemory;
+ labels = isc_mem_get(mctx, n * sizeof(dns_name_t *));
+ if (labels == NULL)
+ goto nomemory;
+
+ if (ipkl->addrs != NULL) {
+ memmove(addrs, ipkl->addrs,
+ ipkl->allocated * sizeof(isc_sockaddr_t));
+ isc_mem_put(mctx, ipkl->addrs,
+ ipkl->allocated * sizeof(isc_sockaddr_t));
+ }
+ ipkl->addrs = addrs;
+ memset(&ipkl->addrs[ipkl->allocated], 0,
+ (n - ipkl->allocated) * sizeof(isc_sockaddr_t));
+
+ if (ipkl->dscps != NULL) {
+ memmove(dscps, ipkl->dscps,
+ ipkl->allocated * sizeof(isc_dscp_t));
+ isc_mem_put(mctx, ipkl->dscps,
+ ipkl->allocated * sizeof(isc_dscp_t));
+ }
+ ipkl->dscps = dscps;
+ memset(&ipkl->dscps[ipkl->allocated], 0,
+ (n - ipkl->allocated) * sizeof(isc_dscp_t));
+
+ if (ipkl->keys) {
+ memmove(keys, ipkl->keys,
+ ipkl->allocated * sizeof(dns_name_t *));
+ isc_mem_put(mctx, ipkl->keys,
+ ipkl->allocated * sizeof(dns_name_t *));
+ }
+ ipkl->keys = keys;
+ memset(&ipkl->keys[ipkl->allocated], 0,
+ (n - ipkl->allocated) * sizeof(dns_name_t *));
+
+ if (ipkl->labels != NULL) {
+ memmove(labels, ipkl->labels,
+ ipkl->allocated * sizeof(dns_name_t *));
+ isc_mem_put(mctx, ipkl->labels,
+ ipkl->allocated * sizeof(dns_name_t *));
+ }
+ ipkl->labels = labels;
+ memset(&ipkl->labels[ipkl->allocated], 0,
+ (n - ipkl->allocated) * sizeof(dns_name_t *));
+
+ ipkl->allocated = n;
+ return (ISC_R_SUCCESS);
+
+nomemory:
+ if (addrs != NULL)
+ isc_mem_put(mctx, addrs, n * sizeof(isc_sockaddr_t));
+ if (dscps != NULL)
+ isc_mem_put(mctx, dscps, n * sizeof(isc_dscp_t));
+ if (keys != NULL)
+ isc_mem_put(mctx, keys, n * sizeof(dns_name_t *));
+ if (labels != NULL)
+ isc_mem_put(mctx, labels, n * sizeof(dns_name_t *));
+
+ return (ISC_R_NOMEMORY);
+}
diff --git a/lib/dns/iptable.c b/lib/dns/iptable.c
new file mode 100644
index 0000000..10713a0
--- /dev/null
+++ b/lib/dns/iptable.c
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/mem.h>
+#include <isc/radix.h>
+#include <isc/util.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 = NULL;
+ isc_mem_attach(mctx, &tab->mctx);
+ isc_refcount_init(&tab->refcount, 1);
+ tab->radix = NULL;
+ 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);
+}
+
+static bool dns_iptable_neg = false;
+static bool dns_iptable_pos = true;
+
+/*
+ * Add an IP prefix to an existing IP table
+ */
+isc_result_t
+dns_iptable_addprefix(dns_iptable_t *tab, isc_netaddr_t *addr,
+ uint16_t bitlen, bool pos)
+{
+ return(dns_iptable_addprefix2(tab, addr, bitlen, pos, false));
+}
+
+isc_result_t
+dns_iptable_addprefix2(dns_iptable_t *tab, isc_netaddr_t *addr,
+ uint16_t bitlen, bool pos,
+ bool is_ecs)
+{
+ isc_result_t result;
+ isc_prefix_t pfx;
+ isc_radix_node_t *node = NULL;
+ int i;
+
+ INSIST(DNS_IPTABLE_VALID(tab));
+ INSIST(tab->radix != NULL);
+
+ NETADDR_TO_PREFIX_T(addr, pfx, bitlen, is_ecs);
+
+ 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 */
+ if (pfx.family == AF_UNSPEC) {
+ /* "any" or "none" */
+ INSIST(pfx.bitlen == 0);
+ for (i = 0; i < RADIX_FAMILIES; i++) {
+ if (node->data[i] == NULL) {
+ node->data[i] = pos ? &dns_iptable_pos
+ : &dns_iptable_neg;
+ }
+ }
+ } else {
+ /* any other prefix */
+ int fam = ISC_RADIX_FAMILY(&pfx);
+ if (node->data[fam] == NULL) {
+ node->data[fam] = pos ? &dns_iptable_pos
+ : &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, bool pos)
+{
+ isc_result_t result;
+ isc_radix_node_t *node, *new_node;
+ int i, 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.
+ */
+ for (i = 0; i < RADIX_FAMILIES; i++) {
+ if (!pos) {
+ if (node->data[i] &&
+ *(bool *) node->data[i])
+ new_node->data[i] = &dns_iptable_neg;
+ }
+ if (node->node_num[i] > max_node)
+ max_node = node->node_num[i];
+ }
+ } 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_putanddetach(&dtab->mctx, dtab, sizeof(*dtab));
+}
diff --git a/lib/dns/journal.c b/lib/dns/journal.c
new file mode 100644
index 0000000..bdbc459
--- /dev/null
+++ b/lib/dns/journal.c
@@ -0,0 +1,2364 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <isc/file.h>
+#include <isc/mem.h>
+#include <isc/print.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 Journaling.
+ *
+ * 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 bool bind8_compat = 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)
+
+#define JOURNAL_SERIALSET 0x01U
+
+static isc_result_t index_to_disk(dns_journal_t *);
+
+static inline 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(uint32_t val, unsigned char *p) {
+ p[0] = (uint8_t)(val >> 24);
+ p[1] = (uint8_t)(val >> 16);
+ p[2] = (uint8_t)(val >> 8);
+ p[3] = (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_fixedname_t fixed;
+ dns_name_t *zonename;
+
+ zonename = dns_fixedname_initname(&fixed);
+ dns_name_copy(dns_db_origin(db), zonename, NULL);
+
+ node = NULL;
+ result = dns_db_findnode(db, zonename, 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);
+ dns_rdataset_getownercase(&rdataset, zonename);
+
+ result = dns_difftuple_create(mctx, op, zonename, rdataset.ttl,
+ &rdata, tp);
+
+ dns_rdataset_disassociate(&rdataset);
+ dns_db_detachnode(db, &node);
+ return (result);
+
+ freenode:
+ dns_db_detachnode(db, &node);
+ nonode:
+ UNEXPECTED_ERROR(__FILE__, __LINE__, "missing SOA");
+ return (result);
+}
+
+/* Journaling */
+
+/*%
+ * 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];
+ /*% Source serial number. */
+ unsigned char sourceserial[4];
+ unsigned char flags;
+ } 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 {
+ 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;
+ uint32_t index_size;
+ uint32_t sourceserial;
+ bool serialset;
+} journal_header_t;
+
+/*%
+ * The in-core representation of the transaction header.
+ */
+
+typedef struct {
+ uint32_t size;
+ uint32_t serial0;
+ uint32_t serial1;
+} journal_xhdr_t;
+
+/*%
+ * The in-core representation of the RR header.
+ */
+typedef struct {
+ 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, 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_INLINE
+} journal_state_t;
+
+struct dns_journal {
+ unsigned int magic; /*%< JOUR */
+ isc_mem_t *mctx; /*%< Memory context */
+ journal_state_t state;
+ 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. */
+ 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 */
+ 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));
+ memmove(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);
+ cooked->sourceserial = decode_uint32(raw->h.sourceserial);
+ cooked->serialset = (raw->h.flags & JOURNAL_SERIALSET);
+}
+
+static void
+journal_header_encode(journal_header_t *cooked, journal_rawheader_t *raw) {
+ unsigned char flags = 0;
+
+ INSIST(sizeof(cooked->format) == sizeof(raw->h.format));
+ memset(raw->pad, 0, sizeof(raw->pad));
+ memmove(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);
+ encode_uint32(cooked->sourceserial, raw->h.sourceserial);
+ if (cooked->serialset)
+ flags |= JOURNAL_SERIALSET;
+ raw->h.flags = flags;
+}
+
+/*
+ * Journal file I/O subroutines, with error checking and reporting.
+ */
+static isc_result_t
+journal_seek(dns_journal_t *j, uint32_t offset) {
+ isc_result_t result;
+
+ result = isc_stdio_seek(j->fp, (off_t)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 += (isc_offset_t)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 += (isc_offset_t)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, uint32_t size,
+ uint32_t serial0, 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);
+ memmove(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, bool writable,
+ bool 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 = NULL;
+ isc_mem_attach(mctx, &j->mctx);
+ j->state = JOURNAL_STATE_INVALID;
+ j->fp = NULL;
+ j->filename = isc_mem_strdup(mctx, filename);
+ j->index = NULL;
+ j->rawindex = NULL;
+
+ if (j->filename == NULL)
+ FAIL(ISC_R_NOMEMORY);
+
+ result = isc_stdio_open(j->filename, writable ? "rb+" : "rb", &fp);
+
+ if (result == ISC_R_FILENOTFOUND) {
+ if (create) {
+ isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_DEBUG(1),
+ "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 unchecked 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 =
+ writable ? JOURNAL_STATE_WRITE : JOURNAL_STATE_READ;
+
+ *journalp = j;
+ return (ISC_R_SUCCESS);
+
+ failure:
+ j->magic = 0;
+ 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->filename != NULL)
+ isc_mem_free(j->mctx, j->filename);
+ if (j->fp != NULL)
+ (void)isc_stdio_close(j->fp);
+ isc_mem_putanddetach(&j->mctx, j, sizeof(*j));
+ return (result);
+}
+
+isc_result_t
+dns_journal_open(isc_mem_t *mctx, const char *filename, unsigned int mode,
+ dns_journal_t **journalp)
+{
+ isc_result_t result;
+ size_t namelen;
+ char backup[1024];
+ bool writable, create;
+
+ create = (mode & DNS_JOURNAL_CREATE);
+ writable = (mode & (DNS_JOURNAL_WRITE|DNS_JOURNAL_CREATE));
+
+ result = journal_open(mctx, filename, writable, create, journalp);
+ if (result == ISC_R_NOTFOUND) {
+ namelen = strlen(filename);
+ if (namelen > 4U && strcmp(filename + namelen - 4, ".jnl") == 0)
+ namelen -= 4;
+
+ result = isc_string_printf(backup, sizeof(backup), "%.*s.jbk",
+ (int)namelen, filename);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ result = journal_open(mctx, backup, writable, writable,
+ 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, 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, 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, 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) {
+ uint32_t offset;
+ isc_result_t result;
+ journal_rawxhdr_t hdr;
+
+ REQUIRE(DNS_JOURNAL_VALID(j));
+ REQUIRE(j->state == JOURNAL_STATE_WRITE ||
+ j->state == JOURNAL_STATE_INLINE);
+
+ /*
+ * 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;
+ uint64_t 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;
+ }
+
+ if (size >= INT32_MAX) {
+ isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR,
+ "dns_journal_writediff: %s: journal entry "
+ "too big to be stored: %" PRIu64 " bytes",
+ j->filename, size);
+ return (ISC_R_NOSPACE);
+ }
+
+ 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, (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;
+ uint64_t total;
+
+ REQUIRE(DNS_JOURNAL_VALID(j));
+ REQUIRE(j->state == JOURNAL_STATE_TRANSACTION ||
+ j->state == JOURNAL_STATE_INLINE);
+
+ /*
+ * Just write out a updated header.
+ */
+ if (j->state == JOURNAL_STATE_INLINE) {
+ CHECK(journal_fsync(j));
+ journal_header_encode(&j->header, &rawheader);
+ CHECK(journal_seek(j, 0));
+ CHECK(journal_write(j, &rawheader, sizeof(rawheader)));
+ CHECK(journal_fsync(j));
+ j->state = JOURNAL_STATE_WRITE;
+ return (ISC_R_SUCCESS);
+ }
+
+ /*
+ * 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);
+ }
+ }
+
+ /*
+ * We currently don't support huge journal entries.
+ */
+ total = j->x.pos[1].offset - j->x.pos[0].offset;
+ if (total >= INT32_MAX) {
+ isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR,
+ "transaction too big to be stored in journal: "
+ "%" PRIu64 "b (max is %" PRIu64 "b)", total,
+ (uint64_t)INT32_MAX);
+ 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));
+
+ if (j->state == JOURNAL_STATE_TRANSACTION) {
+ isc_offset_t offset;
+ offset = (j->x.pos[1].offset - j->x.pos[0].offset) -
+ sizeof(journal_rawxhdr_t);
+ /*
+ * Update the transaction header.
+ */
+ CHECK(journal_seek(j, j->x.pos[0].offset));
+ CHECK(journal_write_xhdr(j, offset, 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->filename != NULL)
+ isc_mem_free(j->mctx, j->filename);
+ if (j->fp != NULL)
+ (void)isc_stdio_close(j->fp);
+ j->magic = 0;
+ isc_mem_putanddetach(&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 */
+ uint32_t db_serial; /* Database SOA serial */
+ 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 unchecked 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;
+ 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 ?
+ true : 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);
+
+ INSIST(ver == NULL);
+
+ 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, DNS_JOURNAL_READ, &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 */
+ uint32_t start_serial; /* Database SOA serial */
+ 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, DNS_JOURNAL_READ, &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);
+ }
+
+ if (j->header.serialset)
+ fprintf(file, "Source serial = %u\n", j->header.sourceserial);
+ dns_diff_init(j->mctx, &diff);
+
+ /*
+ * Set up empty initial buffers for unchecked 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;
+ 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.
+ */
+uint32_t
+dns_journal_first_serial(dns_journal_t *j) {
+ return (j->header.begin.serial);
+}
+
+uint32_t
+dns_journal_last_serial(dns_journal_t *j) {
+ return (j->header.end.serial);
+}
+
+void
+dns_journal_set_sourceserial(dns_journal_t *j, uint32_t sourceserial) {
+
+ REQUIRE(j->state == JOURNAL_STATE_WRITE ||
+ j->state == JOURNAL_STATE_INLINE ||
+ j->state == JOURNAL_STATE_TRANSACTION);
+
+ j->header.sourceserial = sourceserial;
+ j->header.serialset = true;
+ if (j->state == JOURNAL_STATE_WRITE)
+ j->state = JOURNAL_STATE_INLINE;
+}
+
+bool
+dns_journal_get_sourceserial(dns_journal_t *j, uint32_t *sourceserial) {
+ REQUIRE(sourceserial != NULL);
+
+ if (!j->header.serialset)
+ return (false);
+ *sourceserial = j->header.sourceserial;
+ return (true);
+}
+
+/**************************************************************************/
+/*
+ * 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,
+ uint32_t begin_serial, 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;
+ uint32_t ttl;
+ journal_xhdr_t xhdr;
+ journal_rrhdr_t rrhdr;
+
+ if (j->offset > j->it.epos.offset) {
+ isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR,
+ "%s: journal corrupt: possible integer overflow",
+ j->filename);
+ return (ISC_R_UNEXPECTED);
+ }
+ 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, 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;
+ bool 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 = (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);
+}
+
+static isc_result_t
+diff_namespace(dns_db_t *dba, dns_dbversion_t *dbvera,
+ dns_db_t *dbb, dns_dbversion_t *dbverb,
+ unsigned int options, dns_diff_t *resultdiff)
+{
+ dns_db_t *db[2];
+ dns_dbversion_t *ver[2];
+ dns_dbiterator_t *dbit[2] = { NULL, NULL };
+ bool have[2] = { false, false };
+ dns_fixedname_t fixname[2];
+ isc_result_t result, itresult[2];
+ dns_diff_t diff[2];
+ int i, t;
+
+ db[0] = dba, db[1] = dbb;
+ ver[0] = dbvera, ver[1] = dbverb;
+
+ dns_diff_init(resultdiff->mctx, &diff[0]);
+ dns_diff_init(resultdiff->mctx, &diff[1]);
+
+ dns_fixedname_init(&fixname[0]);
+ dns_fixedname_init(&fixname[1]);
+
+ result = dns_db_createiterator(db[0], options, &dbit[0]);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ result = dns_db_createiterator(db[1], options, &dbit[1]);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_iterator;
+
+ 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] = 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] = 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] = false;
+ continue;
+ }
+ if (t > 0) {
+ ISC_LIST_APPENDLIST(resultdiff->tuples,
+ diff[1].tuples, link);
+ INSIST(ISC_LIST_EMPTY(diff[1].tuples));
+ have[1] = 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] = false;
+ next: ;
+ }
+ if (itresult[0] != ISC_R_NOMORE)
+ FAIL(itresult[0]);
+ if (itresult[1] != ISC_R_NOMORE)
+ FAIL(itresult[1]);
+
+ INSIST(ISC_LIST_EMPTY(diff[0].tuples));
+ INSIST(ISC_LIST_EMPTY(diff[1].tuples));
+
+ failure:
+ dns_dbiterator_destroy(&dbit[1]);
+
+ cleanup_iterator:
+ dns_dbiterator_destroy(&dbit[0]);
+ dns_diff_clear(&diff[0]);
+ dns_diff_clear(&diff[1]);
+ 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 *filename)
+{
+ isc_result_t result;
+ dns_diff_t diff;
+
+ dns_diff_init(mctx, &diff);
+
+ result = dns_db_diffx(&diff, dba, dbvera, dbb, dbverb, filename);
+
+ dns_diff_clear(&diff);
+
+ return (result);
+}
+
+isc_result_t
+dns_db_diffx(dns_diff_t *diff, dns_db_t *dba, dns_dbversion_t *dbvera,
+ dns_db_t *dbb, dns_dbversion_t *dbverb, const char *filename)
+{
+ isc_result_t result;
+ dns_journal_t *journal = NULL;
+
+ if (filename != NULL) {
+ result = dns_journal_open(diff->mctx, filename,
+ DNS_JOURNAL_CREATE, &journal);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+
+ CHECK(diff_namespace(dba, dbvera, dbb, dbverb, DNS_DB_NONSEC3, diff));
+ CHECK(diff_namespace(dba, dbvera, dbb, dbverb, DNS_DB_NSEC3ONLY, diff));
+
+ if (journal != NULL) {
+ if (ISC_LIST_EMPTY(diff->tuples))
+ isc_log_write(JOURNAL_DEBUG_LOGARGS(3), "no changes");
+ else
+ CHECK(dns_journal_write_transaction(journal, diff));
+ }
+
+ failure:
+ if (journal != NULL)
+ dns_journal_destroy(&journal);
+ return (result);
+}
+
+isc_result_t
+dns_journal_compact(isc_mem_t *mctx, char *filename, uint32_t serial,
+ uint32_t target_size)
+{
+ unsigned int i;
+ journal_pos_t best_guess;
+ journal_pos_t current_pos;
+ dns_journal_t *j1 = NULL;
+ dns_journal_t *j2 = NULL;
+ journal_rawheader_t rawheader;
+ unsigned int copy_length;
+ size_t namelen;
+ char *buf = NULL;
+ unsigned int size = 0;
+ isc_result_t result;
+ unsigned int indexend;
+ char newname[1024];
+ char backup[1024];
+ bool is_backup = false;
+
+ REQUIRE(filename != NULL);
+
+ namelen = strlen(filename);
+ if (namelen > 4U && strcmp(filename + namelen - 4, ".jnl") == 0)
+ namelen -= 4;
+
+ result = isc_string_printf(newname, sizeof(newname), "%.*s.jnw",
+ (int)namelen, filename);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = isc_string_printf(backup, sizeof(backup), "%.*s.jbk",
+ (int)namelen, filename);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = journal_open(mctx, filename, false, false, &j1);
+ if (result == ISC_R_NOTFOUND) {
+ is_backup = true;
+ result = journal_open(mctx, backup, false, false, &j1);
+ }
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ if (JOURNAL_EMPTY(&j1->header)) {
+ dns_journal_destroy(&j1);
+ return (ISC_R_SUCCESS);
+ }
+
+ if (DNS_SERIAL_GT(j1->header.begin.serial, serial) ||
+ DNS_SERIAL_GT(serial, j1->header.end.serial)) {
+ dns_journal_destroy(&j1);
+ return (ISC_R_RANGE);
+ }
+
+ /*
+ * Cope with very small target sizes.
+ */
+ indexend = sizeof(journal_rawheader_t) +
+ j1->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 ((uint32_t) j1->header.end.offset < target_size) {
+ dns_journal_destroy(&j1);
+ return (ISC_R_SUCCESS);
+ }
+
+ CHECK(journal_open(mctx, newname, true, true, &j2));
+
+ /*
+ * 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 = j1->header.begin;
+ for (i = 0; i < j1->header.index_size; i++) {
+ if (POS_VALID(j1->index[i]) &&
+ DNS_SERIAL_GE(serial, j1->index[i].serial) &&
+ ((uint32_t)(j1->header.end.offset - j1->index[i].offset)
+ >= target_size / 2) &&
+ j1->index[i].offset > best_guess.offset)
+ best_guess = j1->index[i];
+ }
+
+ current_pos = best_guess;
+ while (current_pos.serial != serial) {
+ CHECK(journal_next(j1, &current_pos));
+ if (current_pos.serial == j1->header.end.serial)
+ break;
+
+ if (DNS_SERIAL_GE(serial, current_pos.serial) &&
+ ((uint32_t)(j1->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 != j1->header.end.serial);
+ if (best_guess.serial != serial)
+ CHECK(journal_next(j1, &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 = j1->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(j1, best_guess.offset));
+ CHECK(journal_seek(j2, indexend));
+ for (i = 0; i < copy_length; i += size) {
+ unsigned int len = (copy_length - i) > size ? size :
+ (copy_length - i);
+ CHECK(journal_read(j1, buf, len));
+ CHECK(journal_write(j2, buf, len));
+ }
+
+ CHECK(journal_fsync(j2));
+
+ /*
+ * Compute new header.
+ */
+ j2->header.begin.serial = best_guess.serial;
+ j2->header.begin.offset = indexend;
+ j2->header.end.serial = j1->header.end.serial;
+ j2->header.end.offset = indexend + copy_length;
+ j2->header.sourceserial = j1->header.sourceserial;
+ j2->header.serialset = j1->header.serialset;
+
+ /*
+ * Update the journal header.
+ */
+ journal_header_encode(&j2->header, &rawheader);
+ CHECK(journal_seek(j2, 0));
+ CHECK(journal_write(j2, &rawheader, sizeof(rawheader)));
+ CHECK(journal_fsync(j2));
+
+ /*
+ * Build new index.
+ */
+ current_pos = j2->header.begin;
+ while (current_pos.serial != j2->header.end.serial) {
+ index_add(j2, &current_pos);
+ CHECK(journal_next(j2, &current_pos));
+ }
+
+ /*
+ * Write index.
+ */
+ CHECK(index_to_disk(j2));
+ CHECK(journal_fsync(j2));
+
+ indexend = j2->header.end.offset;
+ POST(indexend);
+ }
+
+ /*
+ * Close both journals before trying to rename files (this is
+ * necessary on WIN32).
+ */
+ dns_journal_destroy(&j1);
+ dns_journal_destroy(&j2);
+
+ /*
+ * 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
+ * by EEXIST. (If any IXFR's are running in other threads, however,
+ * this will fail, and the journal will not be compacted. But
+ * if so, hopefully they'll be finished by the next time we
+ * compact.)
+ */
+ if (rename(newname, filename) == -1) {
+ if (errno == EEXIST && !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;
+ }
+ }
+
+ result = ISC_R_SUCCESS;
+
+ failure:
+ (void)isc_file_remove(newname);
+ if (buf != NULL)
+ isc_mem_put(mctx, buf, size);
+ if (j1 != NULL)
+ dns_journal_destroy(&j1);
+ if (j2 != NULL)
+ dns_journal_destroy(&j2);
+ 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..16dadf8
--- /dev/null
+++ b/lib/dns/key.c
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#include <config.h>
+
+#include <stddef.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include <isc/region.h>
+#include <isc/util.h>
+
+#include <dns/keyvalues.h>
+
+#include <dst/dst.h>
+
+#include "dst_internal.h"
+
+uint16_t
+dst_region_computeid(const isc_region_t *source, unsigned int alg) {
+ 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 ((uint16_t)(ac & 0xffff));
+}
+
+uint16_t
+dst_region_computerid(const isc_region_t *source, unsigned int alg) {
+ 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]);
+
+ ac = ((*p) << 8) + *(p + 1);
+ ac |= DNS_KEYFLAG_REVOKE;
+ for (size -= 2, p +=2; size > 1; size -= 2, p += 2)
+ ac += ((*p) << 8) + *(p + 1);
+
+ if (size > 0)
+ ac += ((*p) << 8);
+ ac += (ac >> 16) & 0xffff;
+
+ return ((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);
+}
+
+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_keytag_t
+dst_key_rid(const dst_key_t *key) {
+ REQUIRE(VALID_KEY(key));
+ return (key->key_rid);
+}
+
+dns_rdataclass_t
+dst_key_class(const dst_key_t *key) {
+ REQUIRE(VALID_KEY(key));
+ return (key->key_class);
+}
+
+bool
+dst_key_iszonekey(const dst_key_t *key) {
+ REQUIRE(VALID_KEY(key));
+
+ if ((key->key_flags & DNS_KEYTYPE_NOAUTH) != 0)
+ return (false);
+ if ((key->key_flags & DNS_KEYFLAG_OWNERMASK) != DNS_KEYOWNER_ZONE)
+ return (false);
+ if (key->key_proto != DNS_KEYPROTO_DNSSEC &&
+ key->key_proto != DNS_KEYPROTO_ANY)
+ return (false);
+ return (true);
+}
+
+bool
+dst_key_isnullkey(const dst_key_t *key) {
+ REQUIRE(VALID_KEY(key));
+
+ if ((key->key_flags & DNS_KEYFLAG_TYPEMASK) != DNS_KEYTYPE_NOKEY)
+ return (false);
+ if ((key->key_flags & DNS_KEYFLAG_OWNERMASK) != DNS_KEYOWNER_ZONE)
+ return (false);
+ if (key->key_proto != DNS_KEYPROTO_DNSSEC &&
+ key->key_proto != DNS_KEYPROTO_ANY)
+ return (false);
+ return (true);
+}
+
+void
+dst_key_setbits(dst_key_t *key, 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;
+}
+
+uint16_t
+dst_key_getbits(const dst_key_t *key) {
+ REQUIRE(VALID_KEY(key));
+ return (key->key_bits);
+}
+
+void
+dst_key_setttl(dst_key_t *key, dns_ttl_t ttl) {
+ REQUIRE(VALID_KEY(key));
+ key->key_ttl = ttl;
+}
+
+dns_ttl_t
+dst_key_getttl(const dst_key_t *key) {
+ REQUIRE(VALID_KEY(key));
+ return (key->key_ttl);
+}
+
+/*! \file */
diff --git a/lib/dns/keydata.c b/lib/dns/keydata.c
new file mode 100644
index 0000000..135d968
--- /dev/null
+++ b/lib/dns/keydata.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <inttypes.h>
+
+#include <isc/buffer.h>
+#include <isc/mem.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dns/rdata.h>
+#include <dns/rdatastruct.h>
+#include <dns/keydata.h>
+
+isc_result_t
+dns_keydata_todnskey(dns_rdata_keydata_t *keydata,
+ dns_rdata_dnskey_t *dnskey, isc_mem_t *mctx)
+{
+ REQUIRE(keydata != NULL && dnskey != NULL);
+
+ dnskey->common.rdtype = dns_rdatatype_dnskey;
+ dnskey->common.rdclass = keydata->common.rdclass;
+ dnskey->mctx = mctx;
+ dnskey->flags = keydata->flags;
+ dnskey->protocol = keydata->protocol;
+ dnskey->algorithm = keydata->algorithm;
+
+ dnskey->datalen = keydata->datalen;
+
+ if (mctx == NULL)
+ dnskey->data = keydata->data;
+ else {
+ dnskey->data = isc_mem_allocate(mctx, dnskey->datalen);
+ if (dnskey->data == NULL)
+ return (ISC_R_NOMEMORY);
+ memmove(dnskey->data, keydata->data, dnskey->datalen);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_keydata_fromdnskey(dns_rdata_keydata_t *keydata,
+ dns_rdata_dnskey_t *dnskey,
+ uint32_t refresh, uint32_t addhd,
+ uint32_t removehd, isc_mem_t *mctx)
+{
+ REQUIRE(keydata != NULL && dnskey != NULL);
+
+ keydata->common.rdtype = dns_rdatatype_keydata;
+ keydata->common.rdclass = dnskey->common.rdclass;
+ keydata->mctx = mctx;
+ keydata->refresh = refresh;
+ keydata->addhd = addhd;
+ keydata->removehd = removehd;
+ keydata->flags = dnskey->flags;
+ keydata->protocol = dnskey->protocol;
+ keydata->algorithm = dnskey->algorithm;
+
+ keydata->datalen = dnskey->datalen;
+ if (mctx == NULL)
+ keydata->data = dnskey->data;
+ else {
+ keydata->data = isc_mem_allocate(mctx, keydata->datalen);
+ if (keydata->data == NULL)
+ return (ISC_R_NOMEMORY);
+ memmove(keydata->data, dnskey->data, keydata->datalen);
+ }
+
+ return (ISC_R_SUCCESS);
+}
diff --git a/lib/dns/keytable.c b/lib/dns/keytable.c
new file mode 100644
index 0000000..57b8608
--- /dev/null
+++ b/lib/dns/keytable.c
@@ -0,0 +1,768 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdbool.h>
+
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/refcount.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>
+
+#define KEYTABLE_MAGIC ISC_MAGIC('K', 'T', 'b', 'l')
+#define VALID_KEYTABLE(kt) ISC_MAGIC_VALID(kt, KEYTABLE_MAGIC)
+
+#define KEYNODE_MAGIC ISC_MAGIC('K', 'N', 'o', 'd')
+#define VALID_KEYNODE(kn) ISC_MAGIC_VALID(kn, KEYNODE_MAGIC)
+
+struct dns_keytable {
+ /* Unlocked. */
+ unsigned int magic;
+ isc_mem_t *mctx;
+ isc_refcount_t active_nodes;
+ isc_refcount_t references;
+ isc_rwlock_t rwlock;
+ /* Locked by rwlock. */
+ dns_rbt_t *table;
+};
+
+struct dns_keynode {
+ unsigned int magic;
+ isc_refcount_t refcount;
+ dst_key_t * key;
+ bool managed;
+ struct dns_keynode * next;
+};
+
+static void
+free_keynode(void *node, void *arg) {
+ dns_keynode_t *keynode = node;
+ isc_mem_t *mctx = arg;
+
+ dns_keynode_detachall(mctx, &keynode);
+}
+
+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_rwlock_init(&keytable->rwlock, 0, 0);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup_rbt;
+ }
+
+ result = isc_refcount_init(&keytable->active_nodes, 0);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup_rwlock;
+ }
+
+ result = isc_refcount_init(&keytable->references, 1);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup_active_nodes;
+ }
+
+ keytable->mctx = NULL;
+ isc_mem_attach(mctx, &keytable->mctx);
+ keytable->magic = KEYTABLE_MAGIC;
+ *keytablep = keytable;
+
+ return (ISC_R_SUCCESS);
+
+ cleanup_active_nodes:
+ isc_refcount_destroy(&keytable->active_nodes);
+
+ cleanup_rwlock:
+ isc_rwlock_destroy(&keytable->rwlock);
+
+ cleanup_rbt:
+ dns_rbt_destroy(&keytable->table);
+
+ cleanup_keytable:
+ isc_mem_putanddetach(&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);
+
+ isc_refcount_increment(&source->references, NULL);
+
+ *targetp = source;
+}
+
+void
+dns_keytable_detach(dns_keytable_t **keytablep) {
+ dns_keytable_t *keytable;
+ unsigned int refs;
+
+ /*
+ * Detach *keytablep from its keytable.
+ */
+
+ REQUIRE(keytablep != NULL && VALID_KEYTABLE(*keytablep));
+
+ keytable = *keytablep;
+ *keytablep = NULL;
+
+ isc_refcount_decrement(&keytable->references, &refs);
+ if (refs == 0) {
+ INSIST(isc_refcount_current(&keytable->active_nodes) == 0);
+ isc_refcount_destroy(&keytable->active_nodes);
+ isc_refcount_destroy(&keytable->references);
+ dns_rbt_destroy(&keytable->table);
+ isc_rwlock_destroy(&keytable->rwlock);
+ keytable->magic = 0;
+ isc_mem_putanddetach(&keytable->mctx,
+ keytable, sizeof(*keytable));
+ }
+}
+
+static isc_result_t
+insert(dns_keytable_t *keytable, bool managed,
+ dns_name_t *keyname, dst_key_t **keyp)
+{
+ isc_result_t result;
+ dns_keynode_t *knode = NULL;
+ dns_rbtnode_t *node;
+
+ REQUIRE(keyp == NULL || *keyp != NULL);
+ REQUIRE(VALID_KEYTABLE(keytable));
+
+ result = dns_keynode_create(keytable->mctx, &knode);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ knode->managed = managed;
+
+ RWLOCK(&keytable->rwlock, isc_rwlocktype_write);
+
+ node = NULL;
+ result = dns_rbt_addnode(keytable->table, keyname, &node);
+
+ if (keyp != NULL) {
+ if (result == ISC_R_EXISTS) {
+ /* Key already in table? */
+ dns_keynode_t *k;
+ for (k = node->data; k != NULL; k = k->next) {
+ if (k->key == NULL) {
+ k->key = *keyp;
+ *keyp = NULL; /* transfer ownership */
+ break;
+ }
+ if (dst_key_compare(k->key, *keyp) == true)
+ break;
+ }
+
+ if (k == NULL)
+ result = ISC_R_SUCCESS;
+ else if (*keyp != NULL)
+ dst_key_free(keyp);
+ }
+
+ if (result == ISC_R_SUCCESS) {
+ knode->key = *keyp;
+ knode->next = node->data;
+ *keyp = NULL;
+ }
+ }
+
+ if (result == ISC_R_SUCCESS) {
+ node->data = knode;
+ knode = NULL;
+ }
+
+ /* Key was already there? That's the same as a success */
+ if (result == ISC_R_EXISTS)
+ result = ISC_R_SUCCESS;
+
+ RWUNLOCK(&keytable->rwlock, isc_rwlocktype_write);
+
+ if (knode != NULL)
+ dns_keynode_detach(keytable->mctx, &knode);
+
+ return (result);
+}
+
+isc_result_t
+dns_keytable_add(dns_keytable_t *keytable, bool managed,
+ dst_key_t **keyp)
+{
+ REQUIRE(keyp != NULL && *keyp != NULL);
+ return (insert(keytable, managed, dst_key_name(*keyp), keyp));
+}
+
+isc_result_t
+dns_keytable_marksecure(dns_keytable_t *keytable, dns_name_t *name) {
+ return (insert(keytable, true, name, NULL));
+}
+
+isc_result_t
+dns_keytable_delete(dns_keytable_t *keytable, dns_name_t *keyname) {
+ isc_result_t result;
+ dns_rbtnode_t *node = NULL;
+
+ REQUIRE(VALID_KEYTABLE(keytable));
+ REQUIRE(keyname != NULL);
+
+ RWLOCK(&keytable->rwlock, isc_rwlocktype_write);
+ result = dns_rbt_findnode(keytable->table, keyname, NULL, &node, NULL,
+ DNS_RBTFIND_NOOPTIONS, NULL, NULL);
+ if (result == ISC_R_SUCCESS) {
+ if (node->data != NULL)
+ result = dns_rbt_deletenode(keytable->table,
+ node, false);
+ else
+ result = ISC_R_NOTFOUND;
+ } else if (result == DNS_R_PARTIALMATCH)
+ result = ISC_R_NOTFOUND;
+ RWUNLOCK(&keytable->rwlock, isc_rwlocktype_write);
+
+ return (result);
+}
+
+isc_result_t
+dns_keytable_deletekeynode(dns_keytable_t *keytable, dst_key_t *dstkey) {
+ isc_result_t result;
+ dns_name_t *keyname;
+ dns_rbtnode_t *node = NULL;
+ dns_keynode_t *knode = NULL, **kprev = NULL;
+
+ REQUIRE(VALID_KEYTABLE(keytable));
+ REQUIRE(dstkey != NULL);
+
+ keyname = dst_key_name(dstkey);
+
+ RWLOCK(&keytable->rwlock, isc_rwlocktype_write);
+ result = dns_rbt_findnode(keytable->table, keyname, NULL, &node, NULL,
+ DNS_RBTFIND_NOOPTIONS, NULL, NULL);
+
+ if (result == DNS_R_PARTIALMATCH)
+ result = ISC_R_NOTFOUND;
+ if (result != ISC_R_SUCCESS)
+ goto finish;
+
+ if (node->data == NULL) {
+ result = ISC_R_NOTFOUND;
+ goto finish;
+ }
+
+ knode = node->data;
+ if (knode->next == NULL && knode->key != NULL &&
+ dst_key_compare(knode->key, dstkey) == true)
+ {
+ result = dns_rbt_deletenode(keytable->table, node, false);
+ goto finish;
+ }
+
+ kprev = (dns_keynode_t **) &node->data;
+ while (knode != NULL) {
+ if (knode->key != NULL &&
+ dst_key_compare(knode->key, dstkey) == true)
+ break;
+ kprev = &knode->next;
+ knode = knode->next;
+ }
+
+ if (knode != NULL) {
+ if (knode->key != NULL)
+ dst_key_free(&knode->key);
+ /*
+ * This is equivalent to:
+ * dns_keynode_attach(knode->next, &tmp);
+ * dns_keynode_detach(kprev);
+ * dns_keynode_attach(tmp, &kprev);
+ * dns_keynode_detach(&tmp);
+ */
+ *kprev = knode->next;
+ knode->next = NULL;
+ dns_keynode_detach(keytable->mctx, &knode);
+ } else
+ result = DNS_R_PARTIALMATCH;
+ finish:
+ RWUNLOCK(&keytable->rwlock, isc_rwlocktype_write);
+ return (result);
+}
+
+isc_result_t
+dns_keytable_find(dns_keytable_t *keytable, dns_name_t *keyname,
+ dns_keynode_t **keynodep)
+{
+ isc_result_t result;
+ dns_rbtnode_t *node = NULL;
+
+ REQUIRE(VALID_KEYTABLE(keytable));
+ REQUIRE(keyname != NULL);
+ REQUIRE(keynodep != NULL && *keynodep == NULL);
+
+ RWLOCK(&keytable->rwlock, isc_rwlocktype_read);
+ result = dns_rbt_findnode(keytable->table, keyname, NULL, &node, NULL,
+ DNS_RBTFIND_NOOPTIONS, NULL, NULL);
+ if (result == ISC_R_SUCCESS) {
+ if (node->data != NULL) {
+ isc_refcount_increment0(&keytable->active_nodes, NULL);
+ dns_keynode_attach(node->data, keynodep);
+ } else
+ result = ISC_R_NOTFOUND;
+ } else if (result == DNS_R_PARTIALMATCH)
+ result = ISC_R_NOTFOUND;
+ RWUNLOCK(&keytable->rwlock, isc_rwlocktype_read);
+
+ return (result);
+}
+
+isc_result_t
+dns_keytable_nextkeynode(dns_keytable_t *keytable, dns_keynode_t *keynode,
+ dns_keynode_t **nextnodep)
+{
+ /*
+ * Return the next key after 'keynode', regardless of
+ * properties.
+ */
+
+ REQUIRE(VALID_KEYTABLE(keytable));
+ REQUIRE(VALID_KEYNODE(keynode));
+ REQUIRE(nextnodep != NULL && *nextnodep == NULL);
+
+ if (keynode->next == NULL)
+ return (ISC_R_NOTFOUND);
+
+ dns_keynode_attach(keynode->next, nextnodep);
+ isc_refcount_increment(&keytable->active_nodes, NULL);
+
+ return (ISC_R_SUCCESS);
+}
+
+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 (knode->key == NULL) {
+ knode = NULL;
+ break;
+ }
+ if (algorithm == dst_key_alg(knode->key)
+ && tag == dst_key_id(knode->key))
+ break;
+ }
+ if (knode != NULL) {
+ isc_refcount_increment0(&keytable->active_nodes, NULL);
+ dns_keynode_attach(knode, keynodep);
+ } 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 (knode->key == NULL) {
+ knode = NULL;
+ break;
+ }
+ 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) {
+ isc_refcount_increment(&keytable->active_nodes, NULL);
+ result = ISC_R_SUCCESS;
+ dns_keynode_attach(knode, nextnodep);
+ } 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_attachkeynode(dns_keytable_t *keytable, dns_keynode_t *source,
+ dns_keynode_t **target)
+{
+ /*
+ * Give back a keynode found via dns_keytable_findkeynode().
+ */
+
+ REQUIRE(VALID_KEYTABLE(keytable));
+ REQUIRE(VALID_KEYNODE(source));
+ REQUIRE(target != NULL && *target == NULL);
+
+ isc_refcount_increment(&keytable->active_nodes, NULL);
+
+ dns_keynode_attach(source, target);
+}
+
+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));
+
+ isc_refcount_decrement(&keytable->active_nodes, NULL);
+ dns_keynode_detach(keytable->mctx, keynodep);
+}
+
+isc_result_t
+dns_keytable_issecuredomain(dns_keytable_t *keytable, dns_name_t *name,
+ dns_name_t *foundname, bool *wantdnssecp)
+{
+ isc_result_t result;
+ dns_rbtnode_t *node = NULL;
+
+ /*
+ * 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);
+
+ result = dns_rbt_findnode(keytable->table, name, foundname, &node,
+ NULL, DNS_RBTFIND_NOOPTIONS, NULL, NULL);
+ if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
+ INSIST(node->data != NULL);
+ *wantdnssecp = true;
+ result = ISC_R_SUCCESS;
+ } else if (result == ISC_R_NOTFOUND) {
+ *wantdnssecp = false;
+ result = ISC_R_SUCCESS;
+ }
+
+ RWUNLOCK(&keytable->rwlock, isc_rwlocktype_read);
+
+ return (result);
+}
+
+static isc_result_t
+putstr(isc_buffer_t **b, const char *str) {
+ isc_result_t result;
+
+ result = isc_buffer_reserve(b, strlen(str));
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ isc_buffer_putstr(*b, str);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_keytable_dump(dns_keytable_t *keytable, FILE *fp) {
+ isc_result_t result;
+ isc_buffer_t *text = NULL;
+
+ REQUIRE(VALID_KEYTABLE(keytable));
+ REQUIRE(fp != NULL);
+
+ result = isc_buffer_allocate(keytable->mctx, &text, 4096);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = dns_keytable_totext(keytable, &text);
+
+ if (isc_buffer_usedlength(text) != 0) {
+ (void) putstr(&text, "\n");
+ } else if (result == ISC_R_SUCCESS)
+ (void) putstr(&text, "none");
+ else {
+ (void) putstr(&text, "could not dump key table: ");
+ (void) putstr(&text, isc_result_totext(result));
+ }
+
+ fprintf(fp, "%.*s", (int) isc_buffer_usedlength(text),
+ (char *) isc_buffer_base(text));
+
+ isc_buffer_free(&text);
+ return (result);
+}
+
+isc_result_t
+dns_keytable_totext(dns_keytable_t *keytable, isc_buffer_t **text) {
+ isc_result_t result;
+ dns_keynode_t *knode;
+ dns_rbtnode_t *node;
+ dns_rbtnodechain_t chain;
+
+ REQUIRE(VALID_KEYTABLE(keytable));
+ REQUIRE(text != NULL && *text != NULL);
+
+ RWLOCK(&keytable->rwlock, isc_rwlocktype_read);
+ dns_rbtnodechain_init(&chain, keytable->mctx);
+ result = dns_rbtnodechain_first(&chain, keytable->table, NULL, NULL);
+ if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
+ if (result == ISC_R_NOTFOUND)
+ result = ISC_R_SUCCESS;
+ goto cleanup;
+ }
+ for (;;) {
+ char pbuf[DST_KEY_FORMATSIZE];
+
+ dns_rbtnodechain_current(&chain, NULL, NULL, &node);
+ for (knode = node->data; knode != NULL; knode = knode->next) {
+ char obuf[DNS_NAME_FORMATSIZE + 200];
+ if (knode->key == NULL)
+ continue;
+ dst_key_format(knode->key, pbuf, sizeof(pbuf));
+ snprintf(obuf, sizeof(obuf), "%s ; %s\n", pbuf,
+ knode->managed ? "managed" : "trusted");
+ result = putstr(text, obuf);
+ if (result != ISC_R_SUCCESS)
+ break;
+ }
+ result = dns_rbtnodechain_next(&chain, NULL, NULL);
+ if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ break;
+ }
+ }
+
+ cleanup:
+ dns_rbtnodechain_invalidate(&chain);
+ RWUNLOCK(&keytable->rwlock, isc_rwlocktype_read);
+ return (result);
+}
+
+isc_result_t
+dns_keytable_forall(dns_keytable_t *keytable,
+ void (*func)(dns_keytable_t *, dns_keynode_t *, void *),
+ void *arg)
+{
+ isc_result_t result;
+ dns_rbtnode_t *node;
+ dns_rbtnodechain_t chain;
+
+ REQUIRE(VALID_KEYTABLE(keytable));
+
+ RWLOCK(&keytable->rwlock, isc_rwlocktype_read);
+ dns_rbtnodechain_init(&chain, keytable->mctx);
+ result = dns_rbtnodechain_first(&chain, keytable->table, NULL, NULL);
+ if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
+ if (result == ISC_R_NOTFOUND)
+ result = ISC_R_SUCCESS;
+ goto cleanup;
+ }
+ isc_refcount_increment0(&keytable->active_nodes, NULL);
+ for (;;) {
+ dns_rbtnodechain_current(&chain, NULL, NULL, &node);
+ if (node->data != NULL)
+ (*func)(keytable, node->data, arg);
+ result = dns_rbtnodechain_next(&chain, NULL, NULL);
+ if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ break;
+ }
+ }
+ isc_refcount_decrement(&keytable->active_nodes, NULL);
+
+ cleanup:
+ dns_rbtnodechain_invalidate(&chain);
+ 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);
+}
+
+bool
+dns_keynode_managed(dns_keynode_t *keynode) {
+ /*
+ * Is this a managed key?
+ */
+ REQUIRE(VALID_KEYNODE(keynode));
+
+ return (keynode->managed);
+}
+
+isc_result_t
+dns_keynode_create(isc_mem_t *mctx, dns_keynode_t **target) {
+ isc_result_t result;
+ dns_keynode_t *knode;
+
+ REQUIRE(target != NULL && *target == NULL);
+
+ knode = isc_mem_get(mctx, sizeof(dns_keynode_t));
+ if (knode == NULL)
+ return (ISC_R_NOMEMORY);
+
+ knode->magic = KEYNODE_MAGIC;
+ knode->managed = false;
+ knode->key = NULL;
+ knode->next = NULL;
+
+ result = isc_refcount_init(&knode->refcount, 1);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ *target = knode;
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_keynode_attach(dns_keynode_t *source, dns_keynode_t **target) {
+ REQUIRE(VALID_KEYNODE(source));
+ isc_refcount_increment(&source->refcount, NULL);
+ *target = source;
+}
+
+void
+dns_keynode_detach(isc_mem_t *mctx, dns_keynode_t **keynode) {
+ unsigned int refs;
+ dns_keynode_t *node = *keynode;
+ REQUIRE(VALID_KEYNODE(node));
+ isc_refcount_decrement(&node->refcount, &refs);
+ if (refs == 0) {
+ if (node->key != NULL)
+ dst_key_free(&node->key);
+ isc_refcount_destroy(&node->refcount);
+ isc_mem_put(mctx, node, sizeof(dns_keynode_t));
+ }
+ *keynode = NULL;
+}
+
+void
+dns_keynode_detachall(isc_mem_t *mctx, dns_keynode_t **keynode) {
+ dns_keynode_t *next = NULL, *node = *keynode;
+ REQUIRE(VALID_KEYNODE(node));
+ while (node != NULL) {
+ next = node->next;
+ dns_keynode_detach(mctx, &node);
+ node = next;
+ }
+ *keynode = NULL;
+}
diff --git a/lib/dns/lib.c b/lib/dns/lib.c
new file mode 100644
index 0000000..304814b
--- /dev/null
+++ b/lib/dns/lib.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $Id: lib.c,v 1.19 2009/09/03 00:12:23 each Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdbool.h>
+#include <stddef.h>
+
+#include <isc/hash.h>
+#include <isc/mem.h>
+#include <isc/msgcat.h>
+#include <isc/mutex.h>
+#include <isc/once.h>
+#include <isc/util.h>
+
+#include <dns/db.h>
+#include <dns/ecdb.h>
+#include <dns/lib.h>
+#include <dns/result.h>
+
+#include <dst/dst.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);
+}
+
+static isc_once_t init_once = ISC_ONCE_INIT;
+static isc_mem_t *dns_g_mctx = NULL;
+static dns_dbimplementation_t *dbimp = NULL;
+static bool initialize_done = false;
+static isc_mutex_t reflock;
+static unsigned int references = 0;
+
+static void
+initialize(void) {
+ isc_result_t result;
+
+ REQUIRE(initialize_done == false);
+
+ result = isc_mem_create(0, 0, &dns_g_mctx);
+ if (result != ISC_R_SUCCESS)
+ return;
+ dns_result_register();
+ result = dns_ecdb_register(dns_g_mctx, &dbimp);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_mctx;
+ result = isc_hash_create(dns_g_mctx, NULL, DNS_NAME_MAXWIRE);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_db;
+
+ result = dst_lib_init(dns_g_mctx, NULL, 0);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_hash;
+
+ result = isc_mutex_init(&reflock);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_dst;
+
+ initialize_done = true;
+ return;
+
+ cleanup_dst:
+ dst_lib_destroy();
+ cleanup_hash:
+ isc_hash_destroy();
+ cleanup_db:
+ if (dbimp != NULL)
+ dns_ecdb_unregister(&dbimp);
+ cleanup_mctx:
+ if (dns_g_mctx != NULL)
+ isc_mem_detach(&dns_g_mctx);
+}
+
+isc_result_t
+dns_lib_init(void) {
+ isc_result_t result;
+
+ /*
+ * Since this routine is expected to be used by a normal application,
+ * it should be better to return an error, instead of an emergency
+ * abort, on any failure.
+ */
+ result = isc_once_do(&init_once, initialize);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ if (!initialize_done)
+ return (ISC_R_FAILURE);
+
+ LOCK(&reflock);
+ references++;
+ UNLOCK(&reflock);
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_lib_shutdown(void) {
+ bool cleanup_ok = false;
+
+ LOCK(&reflock);
+ if (--references == 0)
+ cleanup_ok = true;
+ UNLOCK(&reflock);
+
+ if (!cleanup_ok)
+ return;
+
+ dst_lib_destroy();
+
+ if (isc_hashctx != NULL)
+ isc_hash_destroy();
+ if (dbimp != NULL)
+ dns_ecdb_unregister(&dbimp);
+ if (dns_g_mctx != NULL)
+ isc_mem_detach(&dns_g_mctx);
+}
diff --git a/lib/dns/log.c b/lib/dns/log.c
new file mode 100644
index 0000000..fb1da41
--- /dev/null
+++ b/lib/dns/log.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#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 },
+ { "rpz", 0 },
+ { "rate-limit", 0 },
+ { "cname", 0 },
+ { "spill", 0 },
+ { "dnstap", 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 },
+ { "dns/dnssec", 0 },
+ { "dns/crypto", 0 },
+ { "dns/packets", 0 },
+ { "dns/nta", 0 },
+ { "dns/dyndb", 0 },
+ { "dns/dnstap", 0 },
+ { "dns/ssu", 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..acd9c34
--- /dev/null
+++ b/lib/dns/lookup.c
@@ -0,0 +1,491 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdbool.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;
+ bool 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, 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;
+ bool want_restart;
+ bool 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 = false;
+ send_event = true;
+
+ if (event == NULL && !lookup->canceled) {
+ fname = dns_fixedname_initname(&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 = 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 = true;
+ send_event = 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.
+ */
+ prefix = dns_fixedname_initname(&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 = true;
+ send_event = false;
+ }
+ break;
+ default:
+ send_event = 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 = false;
+ result = ISC_R_QUOTA;
+ send_event = 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 = NULL;
+ isc_mem_attach(mctx, &lookup->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 = 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_putanddetach(&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 = 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_putanddetach(&lookup->mctx, lookup, sizeof(*lookup));
+
+ *lookupp = NULL;
+}
diff --git a/lib/dns/mapapi b/lib/dns/mapapi
new file mode 100644
index 0000000..90bf250
--- /dev/null
+++ b/lib/dns/mapapi
@@ -0,0 +1,16 @@
+# This value should be increased whenever changing the structure of
+# any object that will appear in a type 'map' master file (which
+# contains a working memory image of an RBT database), as loading
+# an incorrect memory image produces an inconsistent and probably
+# nonfunctional database. These structures include but are not
+# necessarily limited to dns_masterrawheader, rbtdb_file_header,
+# rbt_file_header, dns_rbtdb, dns_rbt, dns_rbtnode, rdatasetheader.
+#
+# Err on the side of caution: if anything in the RBTDB is changed,
+# bump the value. Making map files unreadable protects the system
+# from instability; it's a feature not a bug.
+#
+# Whenever releasing a new major release of BIND9, set this value
+# back to 1.0 when releasing the first alpha. Fast files are *never*
+# compatible across major releases.
+MAPAPI=1.0
diff --git a/lib/dns/master.c b/lib/dns/master.c
new file mode 100644
index 0000000..082e63f
--- /dev/null
+++ b/lib/dns/master.c
@@ -0,0 +1,3242 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdbool.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 DNS_RDATA_MAXLENGTH
+/*%
+ * 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)
+
+/*%
+ * Buffers sizes for $GENERATE.
+ */
+#define DNS_MASTER_LHS 2048
+#define DNS_MASTER_RHS MINTSIZ
+
+#define CHECKNAMESFAIL(x) (((x) & DNS_MASTER_CHECKNAMESFAIL) != 0)
+
+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 used by all formats */
+ uint32_t maxttl;
+
+ /* Members specific to the text format: */
+ isc_lex_t *lex;
+ bool keep_lex;
+ unsigned int options;
+ bool ttl_known;
+ bool default_ttl_known;
+ bool warn_1035;
+ bool warn_tcr;
+ bool warn_sigexpired;
+ bool seen_include;
+ uint32_t ttl;
+ 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;
+ bool first;
+ dns_masterrawheader_t header;
+
+ /* Which fixed buffers we are using? */
+ unsigned int loop_cnt; /*% records per quantum,
+ * 0 => all. */
+ bool canceled;
+ isc_mutex_t lock;
+ isc_result_t result;
+ /* locked by lock */
+ uint32_t references;
+ dns_incctx_t *inc;
+ uint32_t resign;
+ isc_stdtime_t now;
+
+ dns_masterincludecb_t include_cb;
+ void *include_arg;
+};
+
+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;
+ bool origin_changed;
+ bool 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
+load_text(dns_loadctx_t *lctx);
+
+static isc_result_t
+openfile_raw(dns_loadctx_t *lctx, const char *master_file);
+
+static isc_result_t
+load_raw(dns_loadctx_t *lctx);
+
+static isc_result_t
+openfile_map(dns_loadctx_t *lctx, const char *master_file);
+
+static isc_result_t
+load_map(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 bool
+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 GETTOKENERR(lexer, options, token, eol, err) \
+ 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 = true; \
+ err \
+ 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 = true; \
+ goto next_line; \
+ } else \
+ goto log_and_cleanup; \
+ } \
+ } while (0)
+#define GETTOKEN(lexer, options, token, eol) \
+ GETTOKENERR(lexer, options, token, eol, {} )
+
+#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, 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 = true; \
+ break; \
+ } 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 dns_name_t const in_addr_arpa =
+ DNS_NAME_INITABSOLUTE(in_addr_arpa_data, in_addr_arpa_offsets);
+
+static unsigned char ip6_int_data[] = "\003IP6\003INT";
+static unsigned char ip6_int_offsets[] = { 0, 4, 8 };
+static dns_name_t const ip6_int =
+ DNS_NAME_INITABSOLUTE(ip6_int_data, ip6_int_offsets);
+
+static unsigned char ip6_arpa_data[] = "\003IP6\004ARPA";
+static unsigned char ip6_arpa_offsets[] = { 0, 4, 9 };
+static dns_name_t const ip6_arpa =
+ DNS_NAME_INITABSOLUTE(ip6_arpa_data, ip6_arpa_offsets);
+
+static inline isc_result_t
+gettoken(isc_lex_t *lex, unsigned int options, isc_token_t *token,
+ bool 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 != true)
+ if (token->type == isc_tokentype_eol ||
+ token->type == isc_tokentype_eof) {
+ unsigned long int line;
+ const char *what;
+ const char *file;
+ file = isc_lex_getsourcename(lex);
+ line = isc_lex_getsourceline(lex);
+ if (token->type == isc_tokentype_eol) {
+ line--;
+ what = "line";
+ } else
+ what = "file";
+ (*callbacks->error)(callbacks,
+ "dns_master_load: %s:%lu: unexpected end of %s",
+ file, line, what);
+ 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;
+ bool need_destroy = 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 = 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] = false;
+ }
+
+ ictx->origin_in_use = 0;
+ ictx->origin = dns_fixedname_name(&ictx->fixed[ictx->origin_in_use]);
+ ictx->in_use[ictx->origin_in_use] = 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 = false;
+ ictx->glue_line = 0;
+ ictx->current_line = 0;
+ ictx->origin_changed = true;
+
+ *ictxp = ictx;
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+loadctx_create(dns_masterformat_t format, isc_mem_t *mctx,
+ unsigned int options, 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,
+ dns_masterincludecb_t include_cb, void *include_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->maxttl = 0;
+
+ 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;
+ case dns_masterformat_map:
+ lctx->openfile = openfile_map;
+ lctx->load = load_map;
+ break;
+ }
+
+ if (lex != NULL) {
+ lctx->lex = lex;
+ lctx->keep_lex = true;
+ } else {
+ lctx->lex = NULL;
+ result = isc_lex_create(mctx, TOKENSIZ, &lctx->lex);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_inc;
+ lctx->keep_lex = false;
+ memset(specials, 0, sizeof(specials));
+ specials[0] = 1;
+ specials['('] = 1;
+ specials[')'] = 1;
+ specials['"'] = 1;
+ isc_lex_setspecials(lctx->lex, specials);
+ isc_lex_setcomments(lctx->lex, ISC_LEXCOMMENT_DNSMASTERFILE);
+ }
+
+ lctx->ttl_known = (options & DNS_MASTER_NOTTL);
+ lctx->ttl = 0;
+ lctx->default_ttl_known = lctx->ttl_known;
+ lctx->default_ttl = 0;
+ lctx->warn_1035 = true; /* XXX Argument? */
+ lctx->warn_tcr = true; /* XXX Argument? */
+ lctx->warn_sigexpired = true; /* XXX Argument? */
+ lctx->options = options;
+ lctx->seen_include = false;
+ lctx->zclass = zclass;
+ lctx->resign = resign;
+ lctx->result = ISC_R_SUCCESS;
+ lctx->include_cb = include_cb;
+ lctx->include_arg = include_arg;
+ isc_stdtime_get(&lctx->now);
+
+ lctx->top = dns_fixedname_initname(&lctx->fixed_top);
+ dns_name_toregion(top, &r);
+ dns_name_fromregion(lctx->top, &r);
+
+ lctx->f = NULL;
+ lctx->first = true;
+ dns_master_initrawheader(&lctx->header);
+
+ 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 = 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 const char *hex = "0123456789abcdef0123456789ABCDEF";
+
+/*%
+ * Convert value into a nibble sequence from least significant to most
+ * significant nibble. Zero fill upper most significant nibbles if
+ * required to make the width.
+ *
+ * Returns the number of characters that should have been written without
+ * counting the terminating NUL.
+ */
+static unsigned int
+nibbles(char *numbuf, size_t length, unsigned int width, char mode, int value) {
+ unsigned int count = 0;
+
+ /*
+ * This reserve space for the NUL string terminator.
+ */
+ if (length > 0U) {
+ *numbuf = '\0';
+ length--;
+ }
+ do {
+ char val = hex[(value & 0x0f) + ((mode == 'n') ? 0 : 16)];
+ value >>= 4;
+ if (length > 0U) {
+ *numbuf++ = val;
+ *numbuf = '\0';
+ length--;
+ }
+ if (width > 0)
+ width--;
+ count++;
+ /*
+ * If width is non zero then we need to add a label seperator.
+ * If value is non zero then we need to add another label and
+ * that requires a label seperator.
+ */
+ if (width > 0 || value != 0) {
+ if (length > 0U) {
+ *numbuf++ = '.';
+ *numbuf = '\0';
+ length--;
+ }
+ if (width > 0)
+ width--;
+ count++;
+ }
+ } while (value != 0 || width > 0);
+ return (count);
+}
+
+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;
+ bool nibblemode;
+
+ r.base = buffer;
+ r.length = (unsigned int)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;
+ }
+ nibblemode = false;
+ strlcpy(fmt, "%d", sizeof(fmt));
+ /* Get format specifier. */
+ if (*name == '{' ) {
+ n = sscanf(name, "{%d,%u,%1[doxXnN]}",
+ &delta, &width, mode);
+ switch (n) {
+ case 1:
+ break;
+ case 2:
+ n = snprintf(fmt, sizeof(fmt),
+ "%%0%ud", width);
+ break;
+ case 3:
+ if (mode[0] == 'n' || mode[0] == 'N')
+ nibblemode = true;
+ 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;
+ }
+ if (nibblemode)
+ n = nibbles(numbuf, sizeof(numbuf), width,
+ mode[0], it + delta);
+ else
+ 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
+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 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;
+ int i, n, start, stop, step = 0;
+ dns_incctx_t *ictx;
+ char dummy[2];
+
+ ictx = lctx->inc;
+ callbacks = lctx->callbacks;
+ owner = dns_fixedname_initname(&ownerfixed);
+ ISC_LIST_INIT(head);
+
+ target_mem = isc_mem_get(lctx->mctx, target_size);
+ rhsbuf = isc_mem_get(lctx->mctx, DNS_MASTER_RHS);
+ lhsbuf = isc_mem_get(lctx->mctx, DNS_MASTER_LHS);
+ 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, "%d-%d%1[/]%d", &start, &stop, dummy, &step);
+ if ((n != 2 && n != 4) || (start < 0) || (stop < 0) ||
+ (n == 4 && step < 1) || (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;
+ }
+
+ /*
+ * RFC2930: TKEY and TSIG are not allowed to be loaded
+ * from master files.
+ */
+ if ((lctx->options & DNS_MASTER_ZONE) != 0 &&
+ (lctx->options & DNS_MASTER_SLAVE) == 0 &&
+ dns_rdatatype_ismeta(type))
+ {
+ (*callbacks->error)(callbacks,
+ "%s: %s:%lu: meta RR type '%s'",
+ "$GENERATE",
+ source, line, gtype);
+ result = DNS_R_METATYPE;
+ goto insist_cleanup;
+ }
+
+ for (i = start; i <= stop; i += step) {
+ result = genname(lhs, i, lhsbuf, DNS_MASTER_LHS);
+ if (result != ISC_R_SUCCESS)
+ goto error_cleanup;
+ result = genname(rhs, i, rhsbuf, DNS_MASTER_RHS);
+ 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 &&
+ (lctx->options & DNS_MASTER_KEY) == 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;
+
+ dns_rdatalist_init(&rdatalist);
+ rdatalist.type = type;
+ 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_LHS);
+ if (rhsbuf != NULL)
+ isc_mem_put(lctx->mctx, rhsbuf, DNS_MASTER_RHS);
+ return (result);
+}
+
+static void
+limit_ttl(dns_rdatacallbacks_t *callbacks, const char *source,
+ unsigned int line, 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
+openfile_text(dns_loadctx_t *lctx, const char *master_file) {
+ return (isc_lex_openfile(lctx->lex, master_file));
+}
+
+static int
+find_free_name(dns_incctx_t *incctx) {
+ int i;
+
+ for (i = 0; i < (NBUFS - 1); i++) {
+ if (!incctx->in_use[i]) {
+ break;
+ }
+ }
+ INSIST(!incctx->in_use[i]);
+ return (i);
+}
+
+static isc_result_t
+load_text(dns_loadctx_t *lctx) {
+ dns_rdataclass_t rdclass;
+ dns_rdatatype_t type, covers;
+ uint32_t ttl_offset = 0;
+ dns_name_t *new_name;
+ bool current_has_delegation = false;
+ bool done = false;
+ bool finish_origin = false;
+ bool finish_include = false;
+ bool read_till_eol = false;
+ bool 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;
+ bool explicit_ttl;
+ 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);
+
+
+ /*
+ * 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 = false;
+ line = isc_lex_getsourceline(lctx->lex);
+ GETTOKEN(lctx->lex, ISC_LEXOPT_INITIALWS | ISC_LEXOPT_QSTRING,
+ &token, 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);
+ POST(line);
+ source = isc_lex_getsourcename(lctx->lex);
+ ictx = lctx->inc;
+ continue;
+ }
+ done = true;
+ continue;
+ }
+
+ if (token.type == isc_tokentype_eol) {
+ read_till_eol = false;
+ continue; /* blank line */
+ }
+
+ if (read_till_eol)
+ continue;
+
+ if (token.type == isc_tokentype_initialws) {
+ /*
+ * Still working on the same name.
+ */
+ initialws = 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, false);
+ finish_origin = true;
+ } else if (strcasecmp(DNS_AS_STR(token),
+ "$TTL") == 0) {
+ GETTOKENERR(lctx->lex, 0, &token, false,
+ lctx->ttl = 0;
+ lctx->default_ttl_known = true;);
+ 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 = 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,
+ 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, true);
+
+ if (token.type == isc_tokentype_eol ||
+ token.type == isc_tokentype_eof) {
+ if (token.type == isc_tokentype_eof)
+ WARNUNEXPECTEDEOF(lctx->lex);
+ /*
+ * 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;
+ source =
+ isc_lex_getsourcename(lctx->lex);
+ line = isc_lex_getsourceline(lctx->lex);
+ POST(line);
+ continue;
+ }
+ /*
+ * There is an origin field. Fall through
+ * to domain name processing code and do
+ * the actual inclusion later.
+ */
+ finish_include = true;
+ } else if (strcasecmp(DNS_AS_STR(token),
+ "$DATE") == 0) {
+ int64_t dump_time64;
+ isc_stdtime_t dump_time, current_time;
+ GETTOKEN(lctx->lex, 0, &token, 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, 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, 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 = false;
+ /* CLASS? */
+ GETTOKEN(lctx->lex, 0, &token, false);
+ if (dns_rdataclass_fromtext(&rdclass,
+ &token.value.as_textregion)
+ == ISC_R_SUCCESS) {
+ GETTOKEN(lctx->lex, 0, &token,
+ 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 = true;
+ explicit_ttl = true;
+ GETTOKEN(lctx->lex, 0, &token,
+ false);
+ }
+ /* CLASS? */
+ if (rdclass == 0 &&
+ dns_rdataclass_fromtext(&rdclass,
+ &token.value.as_textregion)
+ == ISC_R_SUCCESS)
+ GETTOKEN(lctx->lex, 0, &token,
+ 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, ISC_LEXOPT_QSTRING,
+ &token, 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 {
+ 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 {
+ goto insist_and_cleanup;
+ }
+ }
+
+ /*
+ * Normal processing resumes.
+ */
+ new_in_use = find_free_name(ictx);
+ new_name =
+ dns_fixedname_initname(&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, 0, NULL);
+ if (MANYERRS(lctx, result)) {
+ SETRESULT(lctx, result);
+ LOGIT(result);
+ read_till_eol = 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] =
+ false;
+ ictx->origin_in_use = new_in_use;
+ ictx->in_use[ictx->origin_in_use] = true;
+ ictx->origin = new_name;
+ ictx->origin_changed = true;
+ finish_origin = false;
+ EXPECTEOL;
+ continue;
+ }
+ if (finish_include) {
+ finish_include = false;
+ EXPECTEOL;
+ 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;
+ ictx->origin_changed = true;
+ source = isc_lex_getsourcename(lctx->lex);
+ line = isc_lex_getsourceline(lctx->lex);
+ POST(line);
+ 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] =
+ 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] =
+ 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] =
+ false;
+ ictx->current_in_use = new_in_use;
+ ictx->in_use[ictx->current_in_use] =
+ true;
+ ictx->current = new_name;
+ current_has_delegation = 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 &&
+ (lctx->options & DNS_MASTER_KEY) == 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 = true;
+ } else
+ ictx->drop = 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 {
+ 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 = false;
+ continue; /* blank line */
+ }
+
+ if (token.type == isc_tokentype_eof) {
+ WARNUNEXPECTEDEOF(lctx->lex);
+ read_till_eol = 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 = true;
+ continue;
+ } else {
+ goto insist_and_cleanup;
+ }
+ }
+
+ if (ictx->origin_changed) {
+ char cbuf[DNS_NAME_FORMATSIZE];
+ char obuf[DNS_NAME_FORMATSIZE];
+ dns_name_format(ictx->current, cbuf,
+ sizeof(cbuf));
+ dns_name_format(ictx->origin, obuf,
+ sizeof(obuf));
+ (*callbacks->warn)(callbacks,
+ "%s:%lu: record with inherited "
+ "owner (%s) immediately after "
+ "$ORIGIN (%s)", source, line,
+ cbuf, obuf);
+ }
+ }
+
+ ictx->origin_changed = false;
+
+ if (dns_rdataclass_fromtext(&rdclass,
+ &token.value.as_textregion)
+ == ISC_R_SUCCESS)
+ GETTOKEN(lctx->lex, 0, &token, false);
+
+ explicit_ttl = false;
+ result = dns_ttl_fromtext(&token.value.as_textregion,
+ &lctx->ttl);
+ if (result == ISC_R_SUCCESS) {
+ limit_ttl(callbacks, source, line, &lctx->ttl);
+ explicit_ttl = true;
+ lctx->ttl_known = true;
+ GETTOKEN(lctx->lex, 0, &token, 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 = true;
+ continue;
+ } else {
+ goto insist_and_cleanup;
+ }
+ }
+
+ if (rdclass == 0 &&
+ dns_rdataclass_fromtext(&rdclass,
+ &token.value.as_textregion)
+ == ISC_R_SUCCESS)
+ GETTOKEN(lctx->lex, 0, &token, 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 = true;
+ continue;
+ } else {
+ 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 = 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 = true;
+ continue;
+ } else {
+ goto insist_and_cleanup;
+ }
+ }
+
+ if (type == dns_rdatatype_ns && ictx->glue == NULL)
+ current_has_delegation = 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;
+ }
+
+ /*
+ * RFC2930: TKEY and TSIG are not allowed to be loaded
+ * from master files.
+ */
+ if ((lctx->options & DNS_MASTER_ZONE) != 0 &&
+ (lctx->options & DNS_MASTER_SLAVE) == 0 &&
+ dns_rdatatype_ismeta(type))
+ {
+ char typename[DNS_RDATATYPE_FORMATSIZE];
+
+ result = DNS_R_METATYPE;
+
+ 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, 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) {
+ bool ok;
+ dns_name_t *name;
+
+ name = (ictx->glue != NULL) ? ictx->glue :
+ ictx->current;
+ ok = dns_rdata_checkowner(name, lctx->zclass, type,
+ 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 (CHECKNAMESFAIL(lctx->options) ||
+ type == dns_rdatatype_nsec3) {
+ (*callbacks->error)(callbacks,
+ "%s:%lu: %s: %s",
+ source, line,
+ namebuf, desc);
+ if (MANYERRS(lctx, result)) {
+ SETRESULT(lctx, result);
+ } else {
+ goto cleanup;
+ }
+ } else {
+ (*callbacks->warn)(callbacks,
+ "%s:%lu: %s: %s",
+ source, line,
+ namebuf, desc);
+ }
+ }
+ if (type == dns_rdatatype_ptr &&
+ !dns_name_isdnssd(name) &&
+ (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 = true;
+ target = target_ft;
+ continue;
+ } else {
+ 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 = 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 = 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 = 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, lctx->now)) {
+ (*callbacks->warn)(callbacks,
+ "%s:%lu: "
+ "signature has expired",
+ source, line);
+ lctx->warn_sigexpired = 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 = 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 minimise 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++];
+ dns_rdatalist_init(this);
+ this->type = type;
+ this->covers = covers;
+ this->rdclass = lctx->zclass;
+ this->ttl = lctx->ttl;
+ 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;
+ }
+
+ if ((lctx->options & DNS_MASTER_CHECKTTL) != 0 &&
+ lctx->ttl > lctx->maxttl)
+ {
+ (callbacks->error)(callbacks,
+ "dns_master_load: %s:%lu: "
+ "TTL %d exceeds configured max-zone-ttl %d",
+ source, line, lctx->ttl, lctx->maxttl);
+ result = ISC_R_RANGE;
+ goto log_and_cleanup;
+ }
+
+ 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 *newctx = NULL;
+ isc_region_t r;
+
+ REQUIRE(master_file != NULL);
+ REQUIRE(DNS_LCTX_VALID(lctx));
+
+ ictx = lctx->inc;
+ lctx->seen_include = true;
+
+ result = incctx_create(lctx->mctx, origin, &newctx);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ /*
+ * Push origin_changed.
+ */
+ newctx->origin_changed = ictx->origin_changed;
+
+ /* Set current domain. */
+ if (ictx->glue != NULL || ictx->current != NULL) {
+ newctx->current_in_use = find_free_name(newctx);
+ newctx->current =
+ dns_fixedname_name(&newctx->fixed[newctx->current_in_use]);
+ newctx->in_use[newctx->current_in_use] = true;
+ dns_name_toregion((ictx->glue != NULL) ?
+ ictx->glue : ictx->current, &r);
+ dns_name_fromregion(newctx->current, &r);
+ newctx->drop = ictx->drop;
+ }
+
+ result = (lctx->openfile)(lctx, master_file);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ newctx->parent = ictx;
+ lctx->inc = newctx;
+
+ if (lctx->include_cb != NULL)
+ lctx->include_cb(master_file, lctx->include_arg);
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ incctx_destroy(lctx->mctx, newctx);
+ return (result);
+}
+
+/*
+ * Fill/check exists buffer with 'len' bytes. Track remaining bytes to be
+ * read when incrementally filling the buffer.
+ */
+static inline isc_result_t
+read_and_check(bool do_read, isc_buffer_t *buffer,
+ size_t len, FILE *f, uint32_t *totallen)
+{
+ isc_result_t result;
+
+ REQUIRE(totallen != NULL);
+
+ 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, (unsigned int)len);
+ if (*totallen < len)
+ return (ISC_R_RANGE);
+ *totallen -= (uint32_t)len;
+ } else if (isc_buffer_remaininglength(buffer) < len)
+ return (ISC_R_RANGE);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+load_header(dns_loadctx_t *lctx) {
+ isc_result_t result = ISC_R_SUCCESS;
+ dns_masterrawheader_t header;
+ dns_rdatacallbacks_t *callbacks;
+ size_t commonlen = sizeof(header.format) + sizeof(header.version);
+ size_t remainder;
+ unsigned char data[sizeof(header)];
+ isc_buffer_t target;
+
+ REQUIRE(DNS_LCTX_VALID(lctx));
+
+ if (lctx->format != dns_masterformat_raw &&
+ lctx->format != dns_masterformat_map)
+ return (ISC_R_NOTIMPLEMENTED);
+
+ callbacks = lctx->callbacks;
+ dns_master_initrawheader(&header);
+
+ INSIST(commonlen <= sizeof(header));
+ isc_buffer_init(&target, data, sizeof(data));
+
+ result = isc_stdio_read(data, 1, commonlen, 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, (unsigned int)commonlen);
+ header.format = isc_buffer_getuint32(&target);
+ if (header.format != lctx->format) {
+ (*callbacks->error)(callbacks, "dns_master_load: "
+ "file format mismatch (not %s)",
+ lctx->format == dns_masterformat_map
+ ? "map"
+ : "raw");
+ return (ISC_R_NOTIMPLEMENTED);
+ }
+
+ header.version = isc_buffer_getuint32(&target);
+
+ switch (header.version) {
+ case 0:
+ remainder = sizeof(header.dumptime);
+ break;
+ case DNS_RAWFORMAT_VERSION:
+ remainder = sizeof(header) - commonlen;
+ break;
+ default:
+ (*callbacks->error)(callbacks,
+ "dns_master_load: "
+ "unsupported file format version");
+ return (ISC_R_NOTIMPLEMENTED);
+ }
+
+ result = isc_stdio_read(data + commonlen, 1, remainder, 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, (unsigned int)remainder);
+ header.dumptime = isc_buffer_getuint32(&target);
+ if (header.version == DNS_RAWFORMAT_VERSION) {
+ header.flags = isc_buffer_getuint32(&target);
+ header.sourceserial = isc_buffer_getuint32(&target);
+ header.lastxfrin = isc_buffer_getuint32(&target);
+ }
+
+ lctx->first = false;
+ lctx->header = header;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+openfile_map(dns_loadctx_t *lctx, const char *master_file) {
+ isc_result_t result;
+
+ result = isc_stdio_open(master_file, "rb", &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);
+}
+
+/*
+ * Load a map format file, using mmap() to access RBT trees directly
+ */
+static isc_result_t
+load_map(dns_loadctx_t *lctx) {
+ isc_result_t result = ISC_R_SUCCESS;
+ dns_rdatacallbacks_t *callbacks;
+
+ REQUIRE(DNS_LCTX_VALID(lctx));
+
+ callbacks = lctx->callbacks;
+
+ if (lctx->first) {
+ result = load_header(lctx);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = (*callbacks->deserialize)
+ (callbacks->deserialize_private,
+ lctx->f, sizeof(dns_masterrawheader_t));
+ }
+
+ return (result);
+}
+
+static isc_result_t
+openfile_raw(dns_loadctx_t *lctx, const char *master_file) {
+ isc_result_t result;
+
+ result = isc_stdio_open(master_file, "rb", &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
+load_raw(dns_loadctx_t *lctx) {
+ isc_result_t result = ISC_R_SUCCESS;
+ bool done = false;
+ unsigned int loop_cnt = 0;
+ dns_rdatacallbacks_t *callbacks;
+ unsigned char namebuf[DNS_NAME_MAXWIRE];
+ dns_fixedname_t fixed;
+ 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, buf;
+ unsigned char *target_mem = NULL;
+ dns_decompress_t dctx;
+
+ callbacks = lctx->callbacks;
+ dns_decompress_init(&dctx, -1, DNS_DECOMPRESS_NONE);
+
+ if (lctx->first) {
+ result = load_header(lctx);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+
+ ISC_LIST_INIT(head);
+ ISC_LIST_INIT(dummy);
+
+ /*
+ * 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);
+
+ name = dns_fixedname_initname(&fixed);
+
+ /*
+ * 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;
+ uint16_t namelen;
+ uint32_t totallen;
+ size_t minlen, readlen;
+ bool sequential_read = 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 = 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(uint16_t) +
+ sizeof(uint16_t) + sizeof(uint16_t) +
+ sizeof(uint32_t) + sizeof(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 = 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, (unsigned int)readlen);
+ totallen -= (uint32_t)readlen;
+
+ /* Construct RRset headers */
+ dns_rdatalist_init(&rdatalist);
+ rdatalist.rdclass = isc_buffer_getuint16(&target);
+ if (lctx->zclass != rdatalist.rdclass) {
+ result = DNS_R_BADCLASS;
+ goto cleanup;
+ }
+ 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 || rdcount > 0xffff) {
+ 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, &totallen);
+ 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, &totallen);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ isc_buffer_setactive(&target, (unsigned int)namelen);
+ result = dns_name_fromwire(name, &target, &dctx, 0, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ if ((lctx->options & DNS_MASTER_CHECKTTL) != 0 &&
+ rdatalist.ttl > lctx->maxttl)
+ {
+ (callbacks->error)(callbacks,
+ "dns_master_load: "
+ "TTL %d exceeds configured "
+ "max-zone-ttl %d",
+ rdatalist.ttl, lctx->maxttl);
+ result = ISC_R_RANGE;
+ goto cleanup;
+ }
+
+ /* Rdata contents. */
+ if (rdcount > rdata_size) {
+ dns_rdata_t *new_rdata = NULL;
+
+ new_rdata = grow_rdata(rdcount + RDSZ, rdata,
+ rdata_size, &head,
+ &dummy, mctx);
+ if (new_rdata == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ rdata_size = rdcount + RDSZ;
+ rdata = new_rdata;
+ }
+
+ continue_read:
+ for (i = 0; i < rdcount; i++) {
+ 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);
+
+ rdcount -= i;
+
+ goto continue_read;
+ }
+
+ /* rdata length */
+ result = read_and_check(sequential_read, &target,
+ sizeof(rdlen), lctx->f,
+ &totallen);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ rdlen = isc_buffer_getuint16(&target);
+
+ /* rdata */
+ result = read_and_check(sequential_read, &target,
+ rdlen, lctx->f, &totallen);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ isc_buffer_setactive(&target, (unsigned int)rdlen);
+ /*
+ * It is safe to have the source active region and
+ * the target available region be the same if
+ * decompression is disabled (see dctx above) and we
+ * are not downcasing names (options == 0).
+ */
+ isc_buffer_init(&buf, isc_buffer_current(&target),
+ (unsigned int)rdlen);
+ result = dns_rdata_fromwire(&rdata[i],
+ rdatalist.rdclass,
+ rdatalist.type, &target,
+ &dctx, 0, &buf);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ 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 || totallen != 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;
+
+ if (result == ISC_R_SUCCESS && callbacks->rawdata != NULL)
+ (*callbacks->rawdata)(callbacks->zone, &lctx->header);
+
+ 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_loadfile5(master_file, top, origin, zclass,
+ options, 0, callbacks, NULL, NULL,
+ mctx, dns_masterformat_text, 0));
+}
+
+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_loadfile5(master_file, top, origin, zclass,
+ options, 0, callbacks, NULL, NULL,
+ mctx, format, 0));
+}
+
+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, uint32_t resign,
+ dns_rdatacallbacks_t *callbacks, isc_mem_t *mctx,
+ dns_masterformat_t format)
+{
+ return (dns_master_loadfile5(master_file, top, origin, zclass,
+ options, resign, callbacks, NULL, NULL,
+ mctx, format, 0));
+}
+
+isc_result_t
+dns_master_loadfile4(const char *master_file, dns_name_t *top,
+ dns_name_t *origin, dns_rdataclass_t zclass,
+ unsigned int options, uint32_t resign,
+ dns_rdatacallbacks_t *callbacks,
+ dns_masterincludecb_t include_cb, void *include_arg,
+ isc_mem_t *mctx, dns_masterformat_t format)
+{
+ return (dns_master_loadfile5(master_file, top, origin, zclass,
+ options, resign, callbacks,
+ include_cb, include_arg,
+ mctx, format, 0));
+}
+
+isc_result_t
+dns_master_loadfile5(const char *master_file, dns_name_t *top,
+ dns_name_t *origin, dns_rdataclass_t zclass,
+ unsigned int options, uint32_t resign,
+ dns_rdatacallbacks_t *callbacks,
+ dns_masterincludecb_t include_cb, void *include_arg,
+ isc_mem_t *mctx, dns_masterformat_t format,
+ dns_ttl_t maxttl)
+{
+ dns_loadctx_t *lctx = NULL;
+ isc_result_t result;
+
+ result = loadctx_create(format, mctx, options, resign, top, zclass,
+ origin, callbacks, NULL, NULL, NULL,
+ include_cb, include_arg, NULL, &lctx);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ lctx->maxttl = maxttl;
+
+ 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_loadfileinc4(master_file, top, origin, zclass,
+ options, 0, callbacks, task, done,
+ done_arg, lctxp, NULL, NULL, 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_loadfileinc4(master_file, top, origin, zclass,
+ options, 0, callbacks, task, done,
+ done_arg, lctxp, NULL, NULL, 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, 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)
+{
+ return (dns_master_loadfileinc4(master_file, top, origin, zclass,
+ options, resign, callbacks, task,
+ done, done_arg, lctxp, NULL, NULL,
+ mctx, format));
+}
+
+isc_result_t
+dns_master_loadfileinc4(const char *master_file, dns_name_t *top,
+ dns_name_t *origin, dns_rdataclass_t zclass,
+ unsigned int options, uint32_t resign,
+ dns_rdatacallbacks_t *callbacks,
+ isc_task_t *task, dns_loaddonefunc_t done,
+ void *done_arg, dns_loadctx_t **lctxp,
+ dns_masterincludecb_t include_cb, void *include_arg,
+ isc_mem_t *mctx, dns_masterformat_t format)
+{
+ options &= ~DNS_MASTER_CHECKTTL;
+ return (dns_master_loadfileinc5(master_file, top, origin, zclass,
+ options, resign, callbacks, task,
+ done, done_arg, lctxp, include_cb,
+ include_arg, mctx, format, 0));
+}
+
+isc_result_t
+dns_master_loadfileinc5(const char *master_file, dns_name_t *top,
+ dns_name_t *origin, dns_rdataclass_t zclass,
+ unsigned int options, uint32_t resign,
+ dns_rdatacallbacks_t *callbacks,
+ isc_task_t *task, dns_loaddonefunc_t done,
+ void *done_arg, dns_loadctx_t **lctxp,
+ dns_masterincludecb_t include_cb, void *include_arg,
+ isc_mem_t *mctx, dns_masterformat_t format,
+ uint32_t maxttl)
+{
+ 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,
+ include_cb, include_arg, NULL, &lctx);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ lctx->maxttl = maxttl;
+
+ 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, 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, 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 = 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, 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, 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 = 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,
+ 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, NULL, NULL, 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 *oldlist, int old_len,
+ rdatalist_head_t *current, rdatalist_head_t *glue,
+ isc_mem_t *mctx)
+{
+ dns_rdatalist_t *newlist;
+ int rdlcount = 0;
+ ISC_LIST(dns_rdatalist_t) save;
+ dns_rdatalist_t *this;
+
+ newlist = isc_mem_get(mctx, new_len * sizeof(*newlist));
+ if (newlist == NULL)
+ return (NULL);
+
+ ISC_LIST_INIT(save);
+ 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);
+ INSIST(rdlcount < new_len);
+ newlist[rdlcount] = *this;
+ ISC_LIST_APPEND(*current, &newlist[rdlcount], link);
+ rdlcount++;
+ }
+
+ ISC_LIST_INIT(save);
+ 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);
+ INSIST(rdlcount < new_len);
+ newlist[rdlcount] = *this;
+ ISC_LIST_APPEND(*glue, &newlist[rdlcount], link);
+ rdlcount++;
+ }
+
+ INSIST(rdlcount == old_len);
+ if (oldlist != NULL)
+ isc_mem_put(mctx, oldlist, old_len * sizeof(*oldlist));
+ return (newlist);
+}
+
+/*
+ * 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 *oldlist, int old_len,
+ rdatalist_head_t *current, rdatalist_head_t *glue,
+ isc_mem_t *mctx)
+{
+ dns_rdata_t *newlist;
+ int rdcount = 0;
+ ISC_LIST(dns_rdata_t) save;
+ dns_rdatalist_t *this;
+ dns_rdata_t *rdata;
+
+ newlist = isc_mem_get(mctx, new_len * sizeof(*newlist));
+ if (newlist == NULL)
+ return (NULL);
+ memset(newlist, 0, new_len * sizeof(*newlist));
+
+ /*
+ * 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);
+ INSIST(rdcount < new_len);
+ newlist[rdcount] = *rdata;
+ ISC_LIST_APPEND(this->rdata, &newlist[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);
+ INSIST(rdcount < new_len);
+ newlist[rdcount] = *rdata;
+ ISC_LIST_APPEND(this->rdata, &newlist[rdcount], link);
+ rdcount++;
+ }
+ this = ISC_LIST_NEXT(this, link);
+ }
+ INSIST(rdcount == old_len || rdcount == 0);
+ if (oldlist != NULL)
+ isc_mem_put(mctx, oldlist, old_len * sizeof(*oldlist));
+ return (newlist);
+}
+
+static uint32_t
+resign_fromlist(dns_rdatalist_t *this, dns_loadctx_t *lctx) {
+ dns_rdata_t *rdata;
+ dns_rdata_rrsig_t sig;
+ uint32_t when;
+
+ rdata = ISC_LIST_HEAD(this->rdata);
+ INSIST(rdata != NULL);
+ (void)dns_rdata_tostruct(rdata, &sig, NULL);
+ if (isc_serial_gt(sig.timesigned, lctx->now))
+ when = lctx->now;
+ else
+ when = sig.timeexpire - lctx->resign;
+
+ rdata = ISC_LIST_NEXT(rdata, link);
+ while (rdata != NULL) {
+ (void)dns_rdata_tostruct(rdata, &sig, NULL);
+ if (isc_serial_gt(sig.timesigned, lctx->now))
+ when = lctx->now;
+ else if (sig.timeexpire - lctx->resign < when)
+ when = sig.timeexpire - lctx->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;
+ dataset.resign = resign_fromlist(this, lctx);
+ }
+ 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 true if one of the NS rdata's contains 'owner'.
+ */
+
+static bool
+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 (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 (true);
+ rdata = ISC_LIST_NEXT(rdata, link);
+ }
+ return (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 = true;
+ UNLOCK(&lctx->lock);
+}
+
+void
+dns_master_initrawheader(dns_masterrawheader_t *header) {
+ memset(header, 0, sizeof(dns_masterrawheader_t));
+}
diff --git a/lib/dns/masterdump.c b/lib/dns/masterdump.c
new file mode 100644
index 0000000..4d09d16
--- /dev/null
+++ b/lib/dns/masterdump.c
@@ -0,0 +1,2127 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include <isc/buffer.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/types.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/ncache.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)
+
+#define CHECK(x) do { \
+ if ((x) != ISC_R_SUCCESS) \
+ goto cleanup; \
+ } while (0)
+
+struct dns_master_style {
+ dns_masterstyle_flags_t 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;
+ unsigned int split_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;
+ bool class_printed;
+ char * linebreak;
+ char linebreak_buf[DNS_TOTEXT_LINEBREAK_MAXLEN];
+ dns_name_t * origin;
+ dns_name_t * neworigin;
+ dns_fixedname_t origin_fixname;
+ uint32_t current_ttl;
+ bool current_ttl_valid;
+} dns_totext_ctx_t;
+
+LIBDNS_EXTERNAL_DATA const dns_master_style_t
+dns_master_style_keyzone = {
+ 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_RRCOMMENT |
+ DNS_STYLEFLAG_MULTILINE |
+ DNS_STYLEFLAG_KEYDATA,
+ 24, 24, 24, 32, 80, 8, UINT_MAX
+};
+
+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_RRCOMMENT |
+ DNS_STYLEFLAG_MULTILINE,
+ 24, 24, 24, 32, 80, 8, UINT_MAX
+};
+
+LIBDNS_EXTERNAL_DATA const dns_master_style_t
+dns_master_style_full = {
+ DNS_STYLEFLAG_COMMENT |
+ DNS_STYLEFLAG_RESIGN,
+ 46, 46, 46, 64, 120, 8, UINT_MAX
+};
+
+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_RRCOMMENT |
+ DNS_STYLEFLAG_MULTILINE,
+ 24, 32, 32, 40, 80, 8, UINT_MAX
+};
+
+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_RRCOMMENT |
+ DNS_STYLEFLAG_TRUST |
+ DNS_STYLEFLAG_NCACHE,
+ 24, 32, 32, 40, 80, 8, UINT_MAX
+};
+
+LIBDNS_EXTERNAL_DATA const dns_master_style_t
+dns_master_style_simple = {
+ 0,
+ 24, 32, 32, 40, 80, 8, UINT_MAX
+};
+
+/*%
+ * 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, UINT_MAX
+};
+
+/*%
+ * Similar, but indented (i.e., prepended with dns_master_indentstr).
+ */
+LIBDNS_EXTERNAL_DATA const dns_master_style_t
+dns_master_style_indent = {
+ DNS_STYLEFLAG_REL_OWNER |
+ DNS_STYLEFLAG_INDENT,
+ 24, 32, 40, 48, 80, 8, UINT_MAX
+};
+
+/*%
+ * Similar, but with each line commented out.
+ */
+LIBDNS_EXTERNAL_DATA const dns_master_style_t
+dns_master_style_comment = {
+ DNS_STYLEFLAG_REL_OWNER |
+ DNS_STYLEFLAG_MULTILINE |
+ DNS_STYLEFLAG_RRCOMMENT |
+ DNS_STYLEFLAG_COMMENTDATA,
+ 24, 32, 40, 48, 80, 8, UINT_MAX
+};
+
+/*%
+ * YAML style
+ */
+LIBDNS_EXTERNAL_DATA const dns_master_style_t
+dns_master_style_yaml = {
+ DNS_STYLEFLAG_YAML |
+ DNS_STYLEFLAG_REL_OWNER |
+ DNS_STYLEFLAG_INDENT,
+ 24, 32, 40, 48, 80, 8, UINT_MAX
+};
+
+/*%
+ * Default indent string.
+ */
+LIBDNS_EXTERNAL_DATA const char *dns_master_indentstr = "\t";
+LIBDNS_EXTERNAL_DATA unsigned int dns_master_indent = 1;
+
+#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;
+ bool canceled;
+ bool first;
+ bool 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;
+ dns_masterrawheader_t header;
+ 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;
+ memmove(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;
+ memmove(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 = 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);
+
+ if ((ctx->style.flags & DNS_STYLEFLAG_INDENT) != 0 ||
+ (ctx->style.flags & DNS_STYLEFLAG_YAML) != 0)
+ {
+ unsigned int i, len = strlen(dns_master_indentstr);
+ for (i = 0; i < dns_master_indent; i++) {
+ if (isc_buffer_availablelength(&buf) < len)
+ return (DNS_R_TEXTTOOLONG);
+ isc_buffer_putstr(&buf, dns_master_indentstr);
+ }
+ }
+
+ if ((ctx->style.flags & DNS_STYLEFLAG_COMMENTDATA) != 0) {
+ isc_buffer_availableregion(&buf, &r);
+ if (r.length < 1)
+ return (DNS_R_TEXTTOOLONG);
+ r.base[0] = ';';
+ 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 indefinitely 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 = false;
+
+ return (ISC_R_SUCCESS);
+}
+
+#define INDENT_TO(col) \
+ do { \
+ if ((ctx->style.flags & DNS_STYLEFLAG_YAML) != 0) { \
+ if ((result = str_totext(" ", target)) \
+ != ISC_R_SUCCESS) \
+ return (result); \
+ } else 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);
+
+ memmove(region.base, source, l);
+ isc_buffer_add(target, l);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+ncache_summary(dns_rdataset_t *rdataset, bool omit_final_dot,
+ isc_buffer_t *target)
+{
+ isc_result_t result = ISC_R_SUCCESS;
+ dns_rdataset_t rds;
+ dns_name_t name;
+
+ dns_rdataset_init(&rds);
+ dns_name_init(&name, NULL);
+
+ do {
+ dns_ncache_current(rdataset, &name, &rds);
+ for (result = dns_rdataset_first(&rds);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rds)) {
+ CHECK(str_totext("; ", target));
+ CHECK(dns_name_totext(&name, omit_final_dot, target));
+ CHECK(str_totext(" ", target));
+ CHECK(dns_rdatatype_totext(rds.type, target));
+ if (rds.type == dns_rdatatype_rrsig) {
+ CHECK(str_totext(" ", target));
+ CHECK(dns_rdatatype_totext(rds.covers, target));
+ CHECK(str_totext(" ...\n", target));
+ } else {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdataset_current(&rds, &rdata);
+ CHECK(str_totext(" ", target));
+ CHECK(dns_rdata_tofmttext(&rdata, dns_rootname,
+ 0, 0, 0, " ", target));
+ CHECK(str_totext("\n", target));
+ }
+ }
+ dns_rdataset_disassociate(&rds);
+ result = dns_rdataset_next(rdataset);
+ } while (result == ISC_R_SUCCESS);
+
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ cleanup:
+ if (dns_rdataset_isassociated(&rds))
+ dns_rdataset_disassociate(&rds);
+
+ return (result);
+}
+
+/*
+ * 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,
+ bool omit_final_dot,
+ isc_buffer_t *target)
+{
+ isc_result_t result;
+ unsigned int column;
+ bool first = true;
+ uint32_t current_ttl;
+ bool current_ttl_valid;
+ dns_rdatatype_t type;
+ unsigned int type_start;
+ dns_fixedname_t fixed;
+ dns_name_t *name = NULL;
+ unsigned int i;
+
+ REQUIRE(DNS_RDATASET_VALID(rdataset));
+
+ rdataset->attributes |= DNS_RDATASETATTR_LOADORDER;
+ result = dns_rdataset_first(rdataset);
+
+ current_ttl = ctx->current_ttl;
+ current_ttl_valid = ctx->current_ttl_valid;
+
+ if (owner_name != NULL) {
+ name = dns_fixedname_initname(&fixed);
+ dns_name_copy(owner_name, name, NULL);
+ dns_rdataset_getownercase(rdataset, name);
+ }
+
+ while (result == ISC_R_SUCCESS) {
+ column = 0;
+
+ /*
+ * Indent?
+ */
+ if ((ctx->style.flags & DNS_STYLEFLAG_INDENT) != 0 ||
+ (ctx->style.flags & DNS_STYLEFLAG_YAML) != 0)
+ for (i = 0; i < dns_master_indent; i++)
+ RETERR(str_totext(dns_master_indentstr,
+ target));
+
+ /*
+ * YAML enumerator?
+ */
+ if ((ctx->style.flags & DNS_STYLEFLAG_YAML) != 0) {
+ RETERR(str_totext("- ", target));
+ }
+
+ /*
+ * Comment?
+ */
+ if ((ctx->style.flags & DNS_STYLEFLAG_COMMENTDATA) != 0)
+ RETERR(str_totext(";", target));
+
+ /*
+ * Owner name.
+ */
+ if (name != NULL &&
+ ! ((ctx->style.flags & DNS_STYLEFLAG_OMIT_OWNER) != 0 &&
+ !first))
+ {
+ unsigned int name_start = target->used;
+ RETERR(dns_name_totext(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);
+ if ((ctx->style.flags & DNS_STYLEFLAG_TTL_UNITS) != 0) {
+ length = target->used;
+ result = dns_ttl_totext2(rdataset->ttl,
+ false, false,
+ target);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ column += target->used - length;
+ } else {
+ 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);
+ memmove(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 = true;
+ }
+ }
+
+ /*
+ * Class.
+ */
+ if ((ctx->style.flags & DNS_STYLEFLAG_NO_CLASS) == 0 &&
+ ((ctx->style.flags & DNS_STYLEFLAG_OMIT_CLASS) == 0 ||
+ ctx->class_printed == false))
+ {
+ unsigned int class_start;
+ INDENT_TO(class_column);
+ class_start = target->used;
+ if ((ctx->style.flags & DNS_STYLEFLAG_UNKNOWNFORMAT) != 0)
+ result = dns_rdataclass_tounknowntext
+ (rdataset->rdclass, target);
+ else
+ result = dns_rdataclass_totext
+ (rdataset->rdclass, target);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ column += (target->used - class_start);
+ }
+
+ /*
+ * Type.
+ */
+
+ if ((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0) {
+ type = rdataset->covers;
+ } else {
+ type = rdataset->type;
+ }
+
+ INDENT_TO(type_column);
+ type_start = target->used;
+ if ((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0)
+ RETERR(str_totext("\\-", target));
+ switch (type) {
+ case dns_rdatatype_keydata:
+#define KEYDATA "KEYDATA"
+ if ((ctx->style.flags & DNS_STYLEFLAG_KEYDATA) != 0) {
+ if (isc_buffer_availablelength(target) <
+ (sizeof(KEYDATA) - 1))
+ return (ISC_R_NOSPACE);
+ isc_buffer_putstr(target, KEYDATA);
+ break;
+ }
+ /* FALLTHROUGH */
+ default:
+ if ((ctx->style.flags & DNS_STYLEFLAG_UNKNOWNFORMAT) != 0)
+ result = dns_rdatatype_tounknowntext(type, target);
+ else
+ 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->attributes & DNS_RDATASETATTR_NEGATIVE) != 0) {
+ if ((ctx->style.flags & DNS_STYLEFLAG_INDENT) != 0 ||
+ (ctx->style.flags & DNS_STYLEFLAG_YAML) != 0)
+ {
+ for (i = 0; i < dns_master_indent; i++)
+ RETERR(str_totext(dns_master_indentstr,
+ target));
+ }
+ if (NXDOMAIN(rdataset))
+ RETERR(str_totext(";-$NXDOMAIN\n", target));
+ else
+ RETERR(str_totext(";-$NXRRSET\n", target));
+ /*
+ * Print a summary of the cached records which make
+ * up the negative response.
+ */
+ RETERR(ncache_summary(rdataset, omit_final_dot,
+ target));
+ break;
+ } else {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_region_t r;
+
+ dns_rdataset_current(rdataset, &rdata);
+
+ RETERR(dns_rdata_tofmttext(&rdata,
+ ctx->origin,
+ (unsigned int)
+ ctx->style.flags,
+ ctx->style.line_length -
+ ctx->style.rdata_column,
+ ctx->style.split_width,
+ 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 = false;
+ result = dns_rdataset_next(rdataset);
+ }
+
+ 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 = 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,
+ bool 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;
+ if ((ctx->style.flags & DNS_STYLEFLAG_UNKNOWNFORMAT) != 0)
+ result = dns_rdataclass_tounknowntext(rdataset->rdclass,
+ target);
+ else
+ 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;
+ if ((ctx->style.flags & DNS_STYLEFLAG_UNKNOWNFORMAT) != 0)
+ result = dns_rdatatype_tounknowntext(rdataset->type,
+ target);
+ else
+ 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,
+ bool omit_final_dot,
+ bool 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,
+ 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,
+ 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 == false ||
+ ctx->current_ttl != rdataset->ttl)
+ {
+ if ((ctx->style.flags & DNS_STYLEFLAG_COMMENT) != 0)
+ {
+ isc_buffer_clear(buffer);
+ result = dns_ttl_totext(rdataset->ttl,
+ 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 = 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,
+ 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 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, 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) {
+ if ((ctx->style.flags & DNS_STYLEFLAG_INDENT) != 0 ||
+ (ctx->style.flags & DNS_STYLEFLAG_YAML) != 0)
+ {
+ unsigned int j;
+ for (j = 0; j < dns_master_indent; j++)
+ fprintf(f, "%s", dns_master_indentstr);
+ }
+ fprintf(f, "; %s\n", dns_trust_totext(rds->trust));
+ }
+ if (((rds->attributes & DNS_RDATASETATTR_NEGATIVE) != 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((uint64_t)rds->resign, &b);
+ if ((ctx->style.flags & DNS_STYLEFLAG_INDENT) != 0 ||
+ (ctx->style.flags & DNS_STYLEFLAG_YAML) != 0)
+ {
+ unsigned int j;
+ for (j = 0; j < dns_master_indent; j++)
+ fprintf(f, "%s", dns_master_indentstr);
+ }
+ 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;
+ uint32_t totallen;
+ uint16_t dlen;
+ isc_region_t r, r_hdr;
+
+ REQUIRE(buffer->length > 0);
+ REQUIRE(DNS_RDATASET_VALID(rdataset));
+
+ rdataset->attributes |= DNS_RDATASETATTR_LOADORDER;
+ 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 = (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;
+
+ dns_rdataset_current(rdataset, &rdata);
+ dns_rdata_toregion(&rdata, &r);
+ INSIST(r.length <= 0xffffU);
+ dlen = (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.attributes & DNS_RDATASETATTR_NEGATIVE) != 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_SUCCESS)
+ return (result);
+ }
+
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+
+ return (result);
+}
+
+static isc_result_t
+dump_rdatasets_map(isc_mem_t *mctx, dns_name_t *name,
+ dns_rdatasetiter_t *rdsiter, dns_totext_ctx_t *ctx,
+ isc_buffer_t *buffer, FILE *f)
+{
+ UNUSED(mctx);
+ UNUSED(name);
+ UNUSED(rdsiter);
+ UNUSED(ctx);
+ UNUSED(buffer);
+ UNUSED(f);
+
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+/*
+ * 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, 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;
+ bool need_destroy = 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 = 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 = true;
+ UNLOCK(&dctx->lock);
+}
+
+static isc_result_t
+flushandsync(FILE *f, isc_result_t result, const char *temp) {
+ bool logit = (result == ISC_R_SUCCESS);
+
+ if (result == ISC_R_SUCCESS)
+ result = isc_stdio_flush(f);
+ if (result != ISC_R_SUCCESS && logit) {
+ if (temp != NULL)
+ isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
+ "dumping to master file: %s: flush: %s",
+ temp, isc_result_totext(result));
+ else
+ isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
+ "dumping to stream: flush: %s",
+ isc_result_totext(result));
+ logit = false;
+ }
+
+ if (result == ISC_R_SUCCESS)
+ result = isc_stdio_sync(f);
+ if (result != ISC_R_SUCCESS && logit) {
+ if (temp != NULL)
+ isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
+ "dumping to master file: %s: fsync: %s",
+ temp, isc_result_totext(result));
+ else
+ isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
+ "dumping to stream: fsync: %s",
+ isc_result_totext(result));
+ }
+ return (result);
+}
+
+static isc_result_t
+closeandrename(FILE *f, isc_result_t result, const char *temp, const char *file)
+{
+ isc_result_t tresult;
+ bool logit = (result == ISC_R_SUCCESS);
+
+ result = flushandsync(f, result, temp);
+ if (result != ISC_R_SUCCESS)
+ logit = 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 = 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;
+ } else
+ result = flushandsync(dctx->f, result, NULL);
+ (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_masterrawheader_t *header)
+{
+ 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 = true;
+ dctx->canceled = false;
+ dctx->file = NULL;
+ dctx->tmpfile = NULL;
+ dctx->format = format;
+ if (header == NULL)
+ dns_master_initrawheader(&dctx->header);
+ else
+ dctx->header = *header;
+
+ switch (format) {
+ case dns_masterformat_text:
+ dctx->dumpsets = dump_rdatasets_text;
+ break;
+ case dns_masterformat_raw:
+ dctx->dumpsets = dump_rdatasets_raw;
+ break;
+ case dns_masterformat_map:
+ dctx->dumpsets = dump_rdatasets_map;
+ 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
+writeheader(dns_dumpctx_t *dctx) {
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_buffer_t buffer;
+ char *bufmem;
+ isc_region_t r;
+ dns_masterrawheader_t rawheader;
+ uint32_t rawversion, now32;
+
+ bufmem = isc_mem_get(dctx->mctx, initial_buffer_length);
+ if (bufmem == NULL)
+ return (ISC_R_NOMEMORY);
+
+ isc_buffer_init(&buffer, bufmem, initial_buffer_length);
+
+ 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:
+ case dns_masterformat_map:
+ r.base = (unsigned char *)&rawheader;
+ r.length = sizeof(rawheader);
+ isc_buffer_region(&buffer, &r);
+#if !defined(STDTIME_ON_32BITS) || (STDTIME_ON_32BITS + 0) != 1
+ /*
+ * We assume isc_stdtime_t is a 32-bit integer,
+ * which should be the case on most platforms.
+ * 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;
+#endif
+ rawversion = 1;
+ if ((dctx->header.flags & DNS_MASTERRAW_COMPAT) != 0)
+ rawversion = 0;
+
+ isc_buffer_putuint32(&buffer, dctx->format);
+ isc_buffer_putuint32(&buffer, rawversion);
+ isc_buffer_putuint32(&buffer, now32);
+
+ if (rawversion == 1) {
+ isc_buffer_putuint32(&buffer, dctx->header.flags);
+ isc_buffer_putuint32(&buffer,
+ dctx->header.sourceserial);
+ isc_buffer_putuint32(&buffer, dctx->header.lastxfrin);
+ }
+
+ 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)
+ break;
+
+ break;
+ default:
+ INSIST(0);
+ }
+
+ isc_mem_put(dctx->mctx, buffer.base, buffer.length);
+ return (result);
+}
+
+static isc_result_t
+dumptostreaminc(dns_dumpctx_t *dctx) {
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_buffer_t buffer;
+ char *bufmem;
+ dns_name_t *name;
+ dns_fixedname_t fixname;
+ unsigned int nodes;
+ 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);
+
+ name = dns_fixedname_initname(&fixname);
+
+ if (dctx->first) {
+ CHECK(writeheader(dctx));
+
+ /*
+ * Fast format is not currently written incrementally,
+ * so we make the call to dns_db_serialize() here.
+ * If the database is anything other than an rbtdb,
+ * this should result in not implemented
+ */
+ if (dctx->format == dns_masterformat_map) {
+ result = dns_db_serialize(dctx->db, dctx->version,
+ dctx->f);
+ goto cleanup;
+ }
+
+ result = dns_dbiterator_first(dctx->dbiter);
+ if (result != ISC_R_SUCCESS && result != ISC_R_NOMORE)
+ goto cleanup;
+
+ dctx->first = 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 cleanup;
+ }
+ 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 cleanup;
+ }
+ 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;
+ 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",
+ dctx, dctx->nodes);
+ }
+ result = DNS_R_CONTINUE;
+ } else if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ cleanup:
+ 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, NULL);
+ 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_dumptostream3(mctx, db, version, style,
+ dns_masterformat_text, NULL, 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)
+{
+ return (dns_master_dumptostream3(mctx, db, version, style,
+ format, NULL, f));
+}
+
+isc_result_t
+dns_master_dumptostream3(isc_mem_t *mctx, dns_db_t *db,
+ dns_dbversion_t *version,
+ const dns_master_style_t *style,
+ dns_masterformat_t format,
+ dns_masterrawheader_t *header, FILE *f)
+{
+ dns_dumpctx_t *dctx = NULL;
+ isc_result_t result;
+
+ result = dumpctx_create(mctx, db, version, style, f, &dctx,
+ format, header);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = dumptostreaminc(dctx);
+ INSIST(result != DNS_R_CONTINUE);
+ dns_dumpctx_detach(&dctx);
+
+ result = flushandsync(f, result, NULL);
+ return (result);
+}
+
+static isc_result_t
+opentmp(isc_mem_t *mctx, dns_masterformat_t format, 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;
+
+ if (format == dns_masterformat_text)
+ result = isc_file_openunique(tempname, &f);
+ else
+ result = isc_file_bopenunique(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_dumpinc3(mctx, db, version, style, filename, task,
+ done, done_arg, dctxp,
+ dns_masterformat_text, NULL));
+}
+
+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)
+{
+ return (dns_master_dumpinc3(mctx, db, version, style, filename, task,
+ done, done_arg, dctxp, format, NULL));
+}
+
+isc_result_t
+dns_master_dumpinc3(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,
+ dns_masterrawheader_t *header)
+{
+ 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, format, filename, &tempname, &f);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = dumpctx_create(mctx, db, version, style, f, &dctx,
+ format, header);
+ 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_dump3(mctx, db, version, style, filename,
+ dns_masterformat_text, NULL));
+}
+
+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)
+{
+ return (dns_master_dump3(mctx, db, version, style, filename,
+ format, NULL));
+}
+
+isc_result_t
+dns_master_dump3(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, dns_masterrawheader_t *header)
+{
+ FILE *f = NULL;
+ isc_result_t result;
+ char *tempname;
+ dns_dumpctx_t *dctx = NULL;
+
+ result = opentmp(mctx, format, filename, &tempname, &f);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = dumpctx_create(mctx, db, version, style, f, &dctx,
+ format, header);
+ 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);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
+ "dumping master file: %s: dump: %s", filename,
+ isc_result_totext(result));
+ (void)isc_stdio_close(f);
+ return (ISC_R_UNEXPECTED);
+ }
+
+ 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);
+}
+
+dns_masterstyle_flags_t
+dns_master_styleflags(const dns_master_style_t *style) {
+ REQUIRE(style != NULL);
+ return (style->flags);
+}
+
+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)
+{
+ return (dns_master_stylecreate2(stylep, flags, ttl_column,
+ class_column, type_column,
+ rdata_column, line_length,
+ tab_width, 0xffffffff, mctx));
+}
+
+isc_result_t
+dns_master_stylecreate2(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,
+ unsigned int split_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;
+ style->split_width = split_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..ac637a2
--- /dev/null
+++ b/lib/dns/message.c
@@ -0,0 +1,4362 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+/***
+ *** Imports
+ ***/
+
+#include <config.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <stdbool.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/rcode.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/ttl.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, (unsigned)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)) { \
+ result = ISC_R_NOSPACE; \
+ goto cleanup; \
+ } 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 64
+#define OFFSET_COUNT 4
+#define RDATA_COUNT 8
+#define RDATALIST_COUNT 8
+#define RDATASET_COUNT 64
+
+/*%
+ * 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"
+};
+
+/*%
+ * "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 aligned 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);
+
+static void
+logfmtpacket(dns_message_t *message, const char *description,
+ isc_sockaddr_t *address, isc_logcategory_t *category,
+ isc_logmodule_t *module, const dns_master_style_t *style,
+ int level, isc_mem_t *mctx);
+
+/*
+ * 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);
+ goto out;
+ }
+
+ 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);
+ }
+ out:
+ if (rdatalist != NULL)
+ dns_rdatalist_init(rdatalist);
+
+ 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->cc_ok = 0;
+ m->cc_bad = 0;
+ m->tkey = 0;
+ m->rdclass_set = 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;
+ msg->cc_ok = 0;
+ msg->cc_bad = 0;
+ }
+}
+
+static void
+msgresetsigs(dns_message_t *msg, bool 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, bool 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, 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 = NULL;
+ isc_mem_attach(mctx, &m->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_setfillcount(m->namepool, NAME_COUNT);
+ 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_setfillcount(m->rdspool, RDATASET_COUNT);
+ isc_mempool_setfreemax(m->rdspool, RDATASET_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_putanddetach(&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, 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, true);
+ isc_mempool_destroy(&msg->namepool);
+ isc_mempool_destroy(&msg->rdspool);
+ msg->magic = 0;
+ isc_mem_putanddetach(&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;
+
+ REQUIRE(name != NULL);
+ REQUIRE(rdataset == NULL || *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);
+ REQUIRE(rdataset == NULL || *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 (ISC_UNLIKELY(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, 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_ERROR(r) \
+ do { \
+ if (best_effort) \
+ seen_problem = true; \
+ else { \
+ result = r; \
+ 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;
+ bool free_name;
+ bool best_effort;
+ bool seen_problem;
+
+ section = &msg->sections[DNS_SECTION_QUESTION];
+
+ best_effort = (options & DNS_MESSAGEPARSE_BESTEFFORT);
+ seen_problem = 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 = 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_ERROR(DNS_R_FORMERR);
+ ISC_LIST_APPEND(*section, name, link);
+ free_name = false;
+ } else {
+ isc_mempool_put(msg->namepool, name);
+ name = name2;
+ name2 = NULL;
+ free_name = 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->rdclass_set == 0) {
+ msg->rdclass = rdclass;
+ msg->rdclass_set = 1;
+ } else if (msg->rdclass != rdclass)
+ DO_ERROR(DNS_R_FORMERR);
+
+ /*
+ * Is this a TKEY query?
+ */
+ if (rdtype == dns_rdatatype_tkey)
+ msg->tkey = 1;
+
+ /*
+ * Can't ask the same question twice.
+ */
+ result = dns_message_find(name, rdclass, rdtype, 0, NULL);
+ if (result == ISC_R_SUCCESS)
+ DO_ERROR(DNS_R_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->rdclass = rdclass;
+
+ 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 bool
+update(dns_section_t section, dns_rdataclass_t rdclass) {
+ if (section == DNS_SECTION_PREREQUISITE)
+ return (rdclass == dns_rdataclass_any ||
+ rdclass == dns_rdataclass_none);
+ if (section == DNS_SECTION_UPDATE)
+ return (rdclass == dns_rdataclass_any);
+ return (false);
+}
+
+/*
+ * Check to confirm that all DNSSEC records (DS, NSEC, NSEC3) have
+ * covering RRSIGs.
+ */
+static bool
+auth_signed(dns_namelist_t *section) {
+ dns_name_t *name;
+
+ for (name = ISC_LIST_HEAD(*section);
+ name != NULL;
+ name = ISC_LIST_NEXT(name, link))
+ {
+ int auth_dnssec = 0, auth_rrsig = 0;
+ dns_rdataset_t *rds;
+
+ for (rds = ISC_LIST_HEAD(name->list);
+ rds != NULL;
+ rds = ISC_LIST_NEXT(rds, link))
+ {
+ switch (rds->type) {
+ case dns_rdatatype_ds:
+ auth_dnssec |= 0x1;
+ break;
+ case dns_rdatatype_nsec:
+ auth_dnssec |= 0x2;
+ break;
+ case dns_rdatatype_nsec3:
+ auth_dnssec |= 0x4;
+ break;
+ case dns_rdatatype_rrsig:
+ break;
+ default:
+ continue;
+ }
+
+ switch (rds->covers) {
+ case dns_rdatatype_ds:
+ auth_rrsig |= 0x1;
+ break;
+ case dns_rdatatype_nsec:
+ auth_rrsig |= 0x2;
+ break;
+ case dns_rdatatype_nsec3:
+ auth_rrsig |= 0x4;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (auth_dnssec != auth_rrsig)
+ return (false);
+ }
+
+ return (true);
+}
+
+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 = NULL;
+ dns_name_t *name2 = NULL;
+ 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;
+ bool free_name = false, free_rdataset = false;
+ bool preserve_order, best_effort, seen_problem;
+ bool issigzero;
+
+ preserve_order = (options & DNS_MESSAGEPARSE_PRESERVEORDER);
+ best_effort = (options & DNS_MESSAGEPARSE_BESTEFFORT);
+ seen_problem = false;
+
+ section = &msg->sections[sectionid];
+
+ for (count = 0; count < msg->counts[sectionid]; count++) {
+ int recstart = source->current;
+ bool skip_name_search, skip_type_search;
+
+ skip_name_search = false;
+ skip_type_search = false;
+ free_rdataset = false;
+
+ name = isc_mempool_get(msg->namepool);
+ if (name == NULL)
+ return (ISC_R_NOMEMORY);
+ free_name = 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->rdclass_set == 0 &&
+ 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->rdclass_set = 1;
+ }
+
+ /*
+ * 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_key /* 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_ERROR(DNS_R_FORMERR);
+
+ /*
+ * If this is not a TKEY query/response then the KEY
+ * record's class needs to match.
+ */
+ if (msg->opcode != dns_opcode_update && !msg->tkey &&
+ rdtype == dns_rdatatype_key &&
+ msg->rdclass != dns_rdataclass_any &&
+ msg->rdclass != rdclass)
+ DO_ERROR(DNS_R_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_ERROR(DNS_R_BADTSIG);
+ msg->sigstart = recstart;
+ skip_name_search = true;
+ skip_type_search = 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) ||
+ sectionid != DNS_SECTION_ADDITIONAL ||
+ msg->opt != NULL)
+ DO_ERROR(DNS_R_FORMERR);
+ skip_name_search = true;
+ skip_type_search = 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_ERROR(DNS_R_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 = false;
+ if (rdtype == dns_rdatatype_rrsig &&
+ rdata->flags == 0) {
+ covers = dns_rdata_covers(rdata);
+ if (covers == 0)
+ DO_ERROR(DNS_R_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_ERROR(DNS_R_BADSIG0);
+ msg->sigstart = recstart;
+ skip_name_search = true;
+ skip_type_search = true;
+ issigzero = true;
+ } else {
+ if (msg->rdclass != dns_rdataclass_any &&
+ msg->rdclass != rdclass)
+ DO_ERROR(DNS_R_FORMERR);
+ }
+ } else
+ covers = 0;
+
+ /*
+ * Check the ownername of NSEC3 records
+ */
+ if (rdtype == dns_rdatatype_nsec3 &&
+ !dns_rdata_checkowner(name, msg->rdclass, rdtype,
+ false)) {
+ result = DNS_R_BADOWNERNAME;
+ goto cleanup;
+ }
+
+ /*
+ * 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 = 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 = 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_ERROR(DNS_R_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)) {
+ dns_rdata_t *first;
+ dns_rdatalist_fromrdataset(rdataset,
+ &rdatalist);
+ first = ISC_LIST_HEAD(rdatalist->rdata);
+ INSIST(first != NULL);
+ if (dns_rdata_compare(rdata, first) != 0)
+ DO_ERROR(DNS_R_FORMERR);
+ }
+ }
+
+ if (result == ISC_R_NOTFOUND) {
+ rdataset = isc_mempool_get(msg->rdspool);
+ if (rdataset == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ free_rdataset = 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;
+
+ dns_rdataset_init(rdataset);
+ RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist,
+ rdataset)
+ == ISC_R_SUCCESS);
+ dns_rdataset_setownercase(rdataset, name);
+
+ if (rdtype != dns_rdatatype_opt &&
+ rdtype != dns_rdatatype_tsig &&
+ !issigzero)
+ {
+ ISC_LIST_APPEND(name->list, rdataset, link);
+ free_rdataset = 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, SIG(0) or TSIG record, remember it.
+ * Also, set the extended rcode for TSIG.
+ *
+ * Note msg->opt, msg->sig0 and msg->tsig will only be
+ * already set if best-effort parsing is enabled otherwise
+ * there will only be at most one of each.
+ */
+ if (rdtype == dns_rdatatype_opt && msg->opt == NULL) {
+ dns_rcode_t ercode;
+
+ msg->opt = rdataset;
+ rdataset = NULL;
+ free_rdataset = false;
+ ercode = (dns_rcode_t)
+ ((msg->opt->ttl & DNS_MESSAGE_EDNSRCODE_MASK)
+ >> 20);
+ msg->rcode |= ercode;
+ isc_mempool_put(msg->namepool, name);
+ free_name = false;
+ } else if (issigzero && msg->sig0 == NULL) {
+ msg->sig0 = rdataset;
+ msg->sig0name = name;
+ rdataset = NULL;
+ free_rdataset = false;
+ free_name = false;
+ } else if (rdtype == dns_rdatatype_tsig && msg->tsig == NULL) {
+ msg->tsig = rdataset;
+ msg->tsigname = name;
+ /* Windows doesn't like TSIG names to be compressed. */
+ msg->tsigname->attributes |= DNS_NAMEATTR_NOCOMPRESS;
+ rdataset = NULL;
+ free_rdataset = false;
+ free_name = 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 = false;
+ }
+ INSIST(free_name == false);
+ INSIST(free_rdataset == false);
+ }
+
+ /*
+ * If any of DS, NSEC or NSEC3 appeared in the
+ * authority section of a query response without
+ * a covering RRSIG, FORMERR
+ */
+ if (sectionid == DNS_SECTION_AUTHORITY &&
+ msg->opcode == dns_opcode_query &&
+ ((msg->flags & DNS_MESSAGEFLAG_QR) != 0) &&
+ ((msg->flags & DNS_MESSAGEFLAG_TC) == 0) &&
+ !preserve_order &&
+ !auth_signed(section))
+ DO_ERROR(DNS_R_FORMERR);
+
+ 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;
+ uint16_t tmpflags;
+ isc_buffer_t origsource;
+ bool seen_problem;
+ bool ignore_tc;
+
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ REQUIRE(source != NULL);
+ REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTPARSE);
+
+ seen_problem = false;
+ ignore_tc = (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;
+ msg->state = DNS_SECTION_QUESTION;
+
+ /*
+ * -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 = 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 = 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 = 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 = 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);
+ memmove(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 == 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 - DNS_MESSAGE_HEADERLEN < 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);
+ memmove(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 bool
+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 (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 (false);
+
+ return (true);
+}
+
+#ifdef ALLOW_FILTER_AAAA
+/*
+ * Decide whether to not answer with an AAAA record and its RRSIG
+ */
+static inline bool
+norender_rdataset(const dns_rdataset_t *rdataset, unsigned int options,
+ dns_section_t sectionid)
+{
+ if (sectionid == DNS_SECTION_QUESTION)
+ return (false);
+
+ switch (rdataset->type) {
+ case dns_rdatatype_ns:
+ if ((options & DNS_MESSAGERENDER_FILTER_AAAA) == 0 ||
+ sectionid != DNS_SECTION_AUTHORITY)
+ return (false);
+ break;
+
+ case dns_rdatatype_aaaa:
+ if ((options & DNS_MESSAGERENDER_FILTER_AAAA) == 0)
+ return (false);
+ break;
+
+ case dns_rdatatype_rrsig:
+ if ((options & DNS_MESSAGERENDER_FILTER_AAAA) == 0 ||
+ (rdataset->covers != dns_rdatatype_ns &&
+ rdataset->covers != dns_rdatatype_aaaa))
+ return (false);
+ if ((rdataset->covers == dns_rdatatype_ns) &&
+ (sectionid != DNS_SECTION_AUTHORITY))
+ return (false);
+ break;
+
+ default:
+ return (false);
+ }
+
+ if (rdataset->rdclass != dns_rdataclass_in)
+ return (false);
+
+ return (true);
+}
+#endif
+
+static isc_result_t
+renderset(dns_rdataset_t *rdataset, dns_name_t *owner_name,
+ dns_compress_t *cctx, isc_buffer_t *target,
+ unsigned int reserved, unsigned int options, unsigned int *countp)
+{
+ isc_result_t result;
+
+ /*
+ * Shrink the space in the buffer by the reserved amount.
+ */
+ if (target->length - target->used < reserved)
+ return (ISC_R_NOSPACE);
+
+ target->length -= reserved;
+ result = dns_rdataset_towire(rdataset, owner_name,
+ cctx, target, options, countp);
+ target->length += reserved;
+
+ return (result);
+}
+
+static void
+maybe_clear_ad(dns_message_t *msg, dns_section_t sectionid) {
+ if (msg->counts[sectionid] == 0 &&
+ (sectionid == DNS_SECTION_ANSWER ||
+ (sectionid == DNS_SECTION_AUTHORITY &&
+ msg->counts[DNS_SECTION_ANSWER] == 0)))
+ msg->flags &= ~DNS_MESSAGEFLAG_AD;
+}
+
+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;
+ bool partial = 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.
+ */
+ if (msg->buffer->length - msg->buffer->used < msg->reserved)
+ return (ISC_R_NOSPACE);
+ msg->buffer->length -= msg->reserved;
+
+ total = 0;
+ if (msg->reserved == 0 && (options & DNS_MESSAGERENDER_PARTIAL) != 0)
+ partial = 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_NOSPACE)
+ msg->flags |= DNS_MESSAGEFLAG_TC;
+ if (result != ISC_R_SUCCESS) {
+ INSIST(st.used < 65536);
+ dns_compress_rollback(msg->cctx,
+ (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;
+
+#ifdef ALLOW_FILTER_AAAA
+ /*
+ * Suppress AAAAs if asked and we are
+ * not doing DNSSEC or are breaking DNSSEC.
+ * Say so in the AD bit if we break DNSSEC.
+ */
+ if (norender_rdataset(rdataset, options, sectionid)) {
+ if (sectionid == DNS_SECTION_ANSWER ||
+ sectionid == DNS_SECTION_AUTHORITY)
+ msg->flags &= ~DNS_MESSAGEFLAG_AD;
+ if (OPTOUT(rdataset))
+ msg->flags &= ~DNS_MESSAGEFLAG_AD;
+ goto next;
+ }
+
+#endif
+ 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 arbitrary 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,
+ (uint16_t)st.used);
+ *(msg->buffer) = st; /* rollback */
+ msg->buffer->length += msg->reserved;
+ msg->counts[sectionid] += total;
+ maybe_clear_ad(msg, sectionid);
+ 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) {
+ 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,
+ (uint16_t)msg->counts[DNS_SECTION_QUESTION]);
+ isc_buffer_putuint16(target,
+ (uint16_t)msg->counts[DNS_SECTION_ANSWER]);
+ isc_buffer_putuint16(target,
+ (uint16_t)msg->counts[DNS_SECTION_AUTHORITY]);
+ isc_buffer_putuint16(target,
+ (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're adding a OPT, TSIG or SIG(0) to a truncated message,
+ * clear all rdatasets from the message except for the question
+ * before adding the OPT, TSIG or SIG(0). If the question doesn't
+ * fit, don't include it.
+ */
+ if ((msg->tsigkey != NULL || msg->sig0key != NULL || msg->opt) &&
+ (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'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 = renderset(msg->opt, dns_rootname, msg->cctx,
+ msg->buffer, msg->reserved, 0, &count);
+ msg->counts[DNS_SECTION_ADDITIONAL] += count;
+ if (result != ISC_R_SUCCESS)
+ 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 = renderset(msg->tsig, msg->tsigname, msg->cctx,
+ msg->buffer, msg->reserved, 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 = renderset(msg->sig0, dns_rootname, msg->cctx,
+ msg->buffer, msg->reserved, 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);
+ REQUIRE(name == NULL || *name == NULL);
+
+ if (type == dns_rdatatype_any) {
+ REQUIRE(rdataset == NULL);
+ } else {
+ REQUIRE(rdataset == NULL || *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 (ISC_UNLIKELY(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, bool want_question_section) {
+ unsigned int clear_from;
+ 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 = false;
+ if (msg->opcode == dns_opcode_update)
+ clear_from = DNS_SECTION_PREREQUISITE;
+ else if (want_question_section) {
+ if (!msg->question_ok)
+ return (DNS_R_FORMERR);
+ clear_from = DNS_SECTION_ANSWER;
+ } else
+ clear_from = DNS_SECTION_QUESTION;
+ msg->from_to_wire = DNS_MESSAGE_INTENTRENDER;
+ msgresetnames(msg, clear_from);
+ msgresetopt(msg);
+ msgresetsigs(msg, true);
+ msginitprivate(msg);
+ /*
+ * We now clear most flags and then set QR, ensuring that the
+ * reply's flags will be in a reasonable state.
+ */
+ if (msg->opcode == dns_opcode_query)
+ msg->flags &= DNS_MESSAGE_REPLYPRESERVE;
+ else
+ msg->flags = 0;
+ 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_rdataset_disassociate(opt);
+ 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_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);
+ INSIST(result == ISC_R_SUCCESS);
+ if (msg->verified_sig &&
+ msg->tsigstatus == dns_rcode_noerror &&
+ tsig.error == dns_rcode_noerror)
+ {
+ result = ISC_R_SUCCESS;
+ } else if ((!msg->verified_sig) ||
+ (msg->tsigstatus != dns_rcode_noerror))
+ {
+ result = DNS_R_TSIGVERIFYFAILURE;
+ } else {
+ INSIST(tsig.error != dns_rcode_noerror);
+ result = DNS_R_TSIGERRORSET;
+ }
+ 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, 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);
+ }
+}
+
+#define INDENT(sp) \
+ do { \
+ unsigned int __i; \
+ dns_masterstyle_flags_t __flags = dns_master_styleflags(sp); \
+ if ((__flags & DNS_STYLEFLAG_INDENT) == 0ULL && \
+ (__flags & DNS_STYLEFLAG_YAML) == 0ULL) \
+ break; \
+ for (__i = 0; __i < dns_master_indent; __i++) { \
+ ADD_STRING(target, dns_master_indentstr); \
+ } \
+ } while (0)
+
+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 = ISC_R_SUCCESS;
+ bool seensoa = false;
+ unsigned int saveindent;
+ dns_masterstyle_flags_t sflags;
+
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ REQUIRE(target != NULL);
+ REQUIRE(VALID_SECTION(section));
+
+ saveindent = dns_master_indent;
+ sflags = dns_master_styleflags(style);
+ if (ISC_LIST_EMPTY(msg->sections[section]))
+ goto cleanup;
+
+
+ INDENT(style);
+ if ((sflags & DNS_STYLEFLAG_YAML) != 0) {
+ if (msg->opcode != dns_opcode_update) {
+ ADD_STRING(target, sectiontext[section]);
+ } else {
+ ADD_STRING(target, updsectiontext[section]);
+ }
+ ADD_STRING(target, "_SECTION:\n");
+ } else 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) {
+ goto cleanup;
+ }
+ if ((sflags & DNS_STYLEFLAG_YAML) != 0) {
+ dns_master_indent++;
+ }
+ 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_ANSWER &&
+ rdataset->type == dns_rdatatype_soa) {
+ if ((flags & DNS_MESSAGETEXTFLAG_OMITSOA) != 0)
+ continue;
+ if (seensoa &&
+ (flags & DNS_MESSAGETEXTFLAG_ONESOA) != 0)
+ continue;
+ seensoa = true;
+ }
+ if (section == DNS_SECTION_QUESTION) {
+ INDENT(style);
+ if ((sflags & DNS_STYLEFLAG_YAML) != 0) {
+ ADD_STRING(target, "- ");
+ } else {
+ 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)
+ goto cleanup;
+ }
+ result = dns_message_nextname(msg, section);
+ } while (result == ISC_R_SUCCESS);
+ if ((sflags & DNS_STYLEFLAG_YAML) != 0) {
+ dns_master_indent--;
+ }
+ if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0 &&
+ (flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0 &&
+ (sflags & DNS_STYLEFLAG_YAML) == 0)
+ {
+ INDENT(style);
+ ADD_STRING(target, "\n");
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+
+ cleanup:
+ dns_master_indent = saveindent;
+ return (result);
+}
+
+static isc_result_t
+render_ecs(isc_buffer_t *ecsbuf, isc_buffer_t *target) {
+ int i;
+ char addr[16], addr_text[64];
+ uint16_t family;
+ uint8_t addrlen, addrbytes, scopelen;
+ isc_result_t result;
+
+ /*
+ * Note: This routine needs to handle malformed ECS options.
+ */
+
+ if (isc_buffer_remaininglength(ecsbuf) < 4)
+ return (DNS_R_OPTERR);
+ family = isc_buffer_getuint16(ecsbuf);
+ addrlen = isc_buffer_getuint8(ecsbuf);
+ scopelen = isc_buffer_getuint8(ecsbuf);
+
+ addrbytes = (addrlen + 7) / 8;
+ if (isc_buffer_remaininglength(ecsbuf) < addrbytes)
+ return (DNS_R_OPTERR);
+
+ if (addrbytes > sizeof(addr))
+ return (DNS_R_OPTERR);
+
+ memset(addr, 0, sizeof(addr));
+ for (i = 0; i < addrbytes; i ++)
+ addr[i] = isc_buffer_getuint8(ecsbuf);
+
+ switch (family) {
+ case 0:
+ if (addrlen != 0U || scopelen != 0U)
+ return (DNS_R_OPTERR);
+ strlcpy(addr_text, "0", sizeof(addr_text));
+ break;
+ case 1:
+ if (addrlen > 32 || scopelen > 32)
+ return (DNS_R_OPTERR);
+ inet_ntop(AF_INET, addr, addr_text, sizeof(addr_text));
+ break;
+ case 2:
+ if (addrlen > 128 || scopelen > 128)
+ return (DNS_R_OPTERR);
+ inet_ntop(AF_INET6, addr, addr_text, sizeof(addr_text));
+ break;
+ default:
+ return (DNS_R_OPTERR);
+ }
+
+ ADD_STRING(target, ": ");
+ ADD_STRING(target, addr_text);
+ snprintf(addr_text, sizeof(addr_text), "/%d/%d", addrlen, scopelen);
+ ADD_STRING(target, addr_text);
+
+ result = ISC_R_SUCCESS;
+
+ cleanup:
+ return (result);
+}
+
+
+static isc_result_t
+dns_message_pseudosectiontoyaml(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 = ISC_R_SUCCESS;
+ char buf[sizeof("1234567890")];
+ uint32_t mbz;
+ dns_rdata_t rdata;
+ isc_buffer_t optbuf;
+ uint16_t optcode, optlen;
+ unsigned char *optdata;
+ unsigned int saveindent = dns_master_indent;
+
+ 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) {
+ goto cleanup;
+ }
+
+ INDENT(style);
+ ADD_STRING(target, "OPT_PSEUDOSECTION:\n");
+ dns_master_indent++;
+
+ INDENT(style);
+ ADD_STRING(target, "EDNS:\n");
+ dns_master_indent++;
+
+ INDENT(style);
+ ADD_STRING(target, "version: ");
+ snprintf(buf, sizeof(buf), "%u",
+ (unsigned int)((ps->ttl & 0x00ff0000) >> 16));
+ ADD_STRING(target, buf);
+ ADD_STRING(target, "\n");
+ INDENT(style);
+ ADD_STRING(target, "flags:");
+ if ((ps->ttl & DNS_MESSAGEEXTFLAG_DO) != 0)
+ ADD_STRING(target, " do");
+ ADD_STRING(target, "\n");
+ mbz = ps->ttl & 0xffff;
+ mbz &= ~DNS_MESSAGEEXTFLAG_DO; /* Known Flags. */
+ if (mbz != 0) {
+ INDENT(style);
+ ADD_STRING(target, "MBZ: ");
+ snprintf(buf, sizeof(buf), "0x%.4x", mbz);
+ ADD_STRING(target, buf);
+ ADD_STRING(target, "\n");
+ }
+ INDENT(style);
+ 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) {
+ result = ISC_R_SUCCESS;
+ goto cleanup;
+ }
+
+ /*
+ * Print EDNS info, if any.
+ *
+ * WARNING: The option contents may be malformed as
+ * dig +ednsopt=value:<content> does not validity
+ * checking.
+ */
+ dns_rdata_init(&rdata);
+ dns_rdataset_current(ps, &rdata);
+
+ isc_buffer_init(&optbuf, rdata.data, rdata.length);
+ isc_buffer_add(&optbuf, rdata.length);
+ while (isc_buffer_remaininglength(&optbuf) != 0) {
+ INSIST(isc_buffer_remaininglength(&optbuf) >= 4U);
+ optcode = isc_buffer_getuint16(&optbuf);
+ optlen = isc_buffer_getuint16(&optbuf);
+ INSIST(isc_buffer_remaininglength(&optbuf) >= optlen);
+
+ if (optcode == DNS_OPT_NSID) {
+ INDENT(style);
+ ADD_STRING(target, "NSID");
+ } else if (optcode == DNS_OPT_COOKIE) {
+ INDENT(style);
+ ADD_STRING(target, "COOKIE");
+ } else if (optcode == DNS_OPT_CLIENT_SUBNET) {
+ isc_buffer_t ecsbuf;
+ INDENT(style);
+ ADD_STRING(target, "CLIENT-SUBNET");
+ isc_buffer_init(&ecsbuf,
+ isc_buffer_current(&optbuf),
+ optlen);
+ isc_buffer_add(&ecsbuf, optlen);
+ result = render_ecs(&ecsbuf, target);
+ if (result == ISC_R_NOSPACE)
+ goto cleanup;
+ if (result == ISC_R_SUCCESS) {
+ isc_buffer_forward(&optbuf, optlen);
+ ADD_STRING(target, "\n");
+ continue;
+ }
+ ADD_STRING(target, "\n");
+ } else if (optcode == DNS_OPT_EXPIRE) {
+ if (optlen == 4) {
+ uint32_t secs;
+ secs = isc_buffer_getuint32(&optbuf);
+ INDENT(style);
+ ADD_STRING(target, "EXPIRE: ");
+ snprintf(buf, sizeof(buf), "%u", secs);
+ ADD_STRING(target, buf);
+ ADD_STRING(target, " (");
+ result = dns_ttl_totext(secs,
+ true,
+ target);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ ADD_STRING(target, ")\n");
+ continue;
+ }
+ INDENT(style);
+ ADD_STRING(target, "EXPIRE");
+ } else if (optcode == DNS_OPT_PAD) {
+ INDENT(style);
+ ADD_STRING(target, "PAD");
+ } else if (optcode == DNS_OPT_KEY_TAG) {
+ INDENT(style);
+ ADD_STRING(target, "KEY-TAG");
+ if (optlen > 0U && (optlen % 2U) == 0U) {
+ const char *sep = ": ";
+ uint16_t id;
+ while (optlen > 0U) {
+ id = isc_buffer_getuint16(&optbuf);
+ snprintf(buf, sizeof(buf), "%s%u",
+ sep, id);
+ ADD_STRING(target, buf);
+ sep = ", ";
+ optlen -= 2;
+ }
+ ADD_STRING(target, "\n");
+ continue;
+ }
+ } else {
+ INDENT(style);
+ ADD_STRING(target, "OPT: ");
+ snprintf(buf, sizeof(buf), "%u", optcode);
+ ADD_STRING(target, buf);
+ ADD_STRING(target, "\n");
+ }
+
+ if (optlen != 0) {
+ int i;
+ ADD_STRING(target, ": ");
+
+ optdata = isc_buffer_current(&optbuf);
+ for (i = 0; i < optlen; i++) {
+ const char *sep;
+ switch (optcode) {
+ case DNS_OPT_COOKIE:
+ sep = "";
+ break;
+ default:
+ sep = " ";
+ break;
+ }
+ snprintf(buf, sizeof(buf), "%02x%s",
+ optdata[i], sep);
+ ADD_STRING(target, buf);
+ }
+
+ isc_buffer_forward(&optbuf, optlen);
+
+ if (optcode == DNS_OPT_COOKIE) {
+ /*
+ * Valid server cookie?
+ */
+ if (msg->cc_ok && optlen >= 16)
+ ADD_STRING(target, " (good)");
+ /*
+ * Server cookie is not valid but
+ * we had our cookie echoed back.
+ */
+ if (msg->cc_ok && optlen < 16)
+ ADD_STRING(target, " (echoed)");
+ /*
+ * We didn't get our cookie echoed
+ * back.
+ */
+ if (msg->cc_bad)
+ ADD_STRING(target, " (bad)");
+ ADD_STRING(target, "\n");
+ continue;
+ }
+
+ if (optcode == DNS_OPT_CLIENT_SUBNET) {
+ ADD_STRING(target, "\n");
+ continue;
+ }
+
+ /*
+ * For non-COOKIE options, add a printable
+ * version
+ */
+ ADD_STRING(target, "(\"");
+ if (isc_buffer_availablelength(target) < optlen)
+ {
+ result = ISC_R_NOSPACE;
+ goto cleanup;
+ }
+ for (i = 0; i < optlen; i++) {
+ if (isprint(optdata[i]))
+ isc_buffer_putmem(target,
+ &optdata[i],
+ 1);
+ else
+ isc_buffer_putstr(target, ".");
+ }
+ ADD_STRING(target, "\")");
+ }
+ ADD_STRING(target, "\n");
+ }
+ result = ISC_R_SUCCESS;
+ goto cleanup;
+ case DNS_PSEUDOSECTION_TSIG:
+ ps = dns_message_gettsig(msg, &name);
+ if (ps == NULL) {
+ result = ISC_R_SUCCESS;
+ goto cleanup;
+ }
+ INDENT(style);
+ ADD_STRING(target, "TSIG_PSEUDOSECTION:\n");
+ result = dns_master_rdatasettotext(name, ps, style, target);
+ ADD_STRING(target, "\n");
+ goto cleanup;
+ case DNS_PSEUDOSECTION_SIG0:
+ ps = dns_message_getsig0(msg, &name);
+ if (ps == NULL) {
+ result = ISC_R_SUCCESS;
+ goto cleanup;
+ }
+ INDENT(style);
+ 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");
+ goto cleanup;
+ }
+
+ result = ISC_R_UNEXPECTED;
+
+ cleanup:
+ dns_master_indent = saveindent;
+ 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")];
+ uint32_t mbz;
+ dns_rdata_t rdata;
+ isc_buffer_t optbuf;
+ uint16_t optcode, optlen;
+ unsigned char *optdata;
+
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ REQUIRE(target != NULL);
+ REQUIRE(VALID_PSEUDOSECTION(section));
+
+ if ((dns_master_styleflags(style) & DNS_STYLEFLAG_YAML) != 0)
+ return (dns_message_pseudosectiontoyaml(msg, section, style,
+ flags, target));
+ switch (section) {
+ case DNS_PSEUDOSECTION_OPT:
+ ps = dns_message_getopt(msg);
+ if (ps == NULL)
+ return (ISC_R_SUCCESS);
+ if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0) {
+ INDENT(style);
+ ADD_STRING(target, ";; OPT PSEUDOSECTION:\n");
+ }
+
+ INDENT(style);
+ 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 & 0xffff;
+ mbz &= ~DNS_MESSAGEEXTFLAG_DO; /* Known Flags. */
+ if (mbz != 0) {
+ ADD_STRING(target, "; MBZ: ");
+ snprintf(buf, sizeof(buf), "0x%.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.
+ *
+ * WARNING: The option contents may be malformed as
+ * dig +ednsopt=value:<content> does not validity
+ * checking.
+ */
+ dns_rdata_init(&rdata);
+ dns_rdataset_current(ps, &rdata);
+
+ isc_buffer_init(&optbuf, rdata.data, rdata.length);
+ isc_buffer_add(&optbuf, rdata.length);
+ while (isc_buffer_remaininglength(&optbuf) != 0) {
+ INSIST(isc_buffer_remaininglength(&optbuf) >= 4U);
+ optcode = isc_buffer_getuint16(&optbuf);
+ optlen = isc_buffer_getuint16(&optbuf);
+ INSIST(isc_buffer_remaininglength(&optbuf) >= optlen);
+
+ INDENT(style);
+
+ if (optcode == DNS_OPT_NSID) {
+ ADD_STRING(target, "; NSID");
+ } else if (optcode == DNS_OPT_COOKIE) {
+ ADD_STRING(target, "; COOKIE");
+ } else if (optcode == DNS_OPT_CLIENT_SUBNET) {
+ isc_buffer_t ecsbuf;
+
+ ADD_STRING(target, "; CLIENT-SUBNET");
+ isc_buffer_init(&ecsbuf,
+ isc_buffer_current(&optbuf),
+ optlen);
+ isc_buffer_add(&ecsbuf, optlen);
+ result = render_ecs(&ecsbuf, target);
+ if (result == ISC_R_NOSPACE)
+ return (result);
+ if (result == ISC_R_SUCCESS) {
+ isc_buffer_forward(&optbuf, optlen);
+ ADD_STRING(target, "\n");
+ continue;
+ }
+ } else if (optcode == DNS_OPT_EXPIRE) {
+ if (optlen == 4) {
+ uint32_t secs;
+ secs = isc_buffer_getuint32(&optbuf);
+ ADD_STRING(target, "; EXPIRE: ");
+ snprintf(buf, sizeof(buf), "%u", secs);
+ ADD_STRING(target, buf);
+ ADD_STRING(target, " (");
+ result = dns_ttl_totext(secs,
+ true,
+ target);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ ADD_STRING(target, ")\n");
+ continue;
+ }
+ ADD_STRING(target, "; EXPIRE");
+ } else if (optcode == DNS_OPT_PAD) {
+ ADD_STRING(target, "; PAD");
+ } else if (optcode == DNS_OPT_KEY_TAG) {
+ ADD_STRING(target, "; KEY-TAG");
+ if (optlen > 0U && (optlen % 2U) == 0U) {
+ const char *sep = ": ";
+ uint16_t id;
+ while (optlen > 0U) {
+ id = isc_buffer_getuint16(&optbuf);
+ snprintf(buf, sizeof(buf), "%s%u",
+ sep, id);
+ ADD_STRING(target, buf);
+ sep = ", ";
+ optlen -= 2;
+ }
+ ADD_STRING(target, "\n");
+ continue;
+ }
+ } else {
+ ADD_STRING(target, "; OPT=");
+ snprintf(buf, sizeof(buf), "%u", optcode);
+ ADD_STRING(target, buf);
+ }
+
+ if (optlen != 0) {
+ int i;
+ ADD_STRING(target, ": ");
+
+ optdata = isc_buffer_current(&optbuf);
+ for (i = 0; i < optlen; i++) {
+ const char *sep;
+ switch (optcode) {
+ case DNS_OPT_COOKIE:
+ sep = "";
+ break;
+ default:
+ sep = " ";
+ break;
+ }
+ snprintf(buf, sizeof(buf), "%02x%s",
+ optdata[i], sep);
+ ADD_STRING(target, buf);
+ }
+
+ isc_buffer_forward(&optbuf, optlen);
+
+ if (optcode == DNS_OPT_COOKIE) {
+ /*
+ * Valid server cookie?
+ */
+ if (msg->cc_ok && optlen >= 16)
+ ADD_STRING(target, " (good)");
+ /*
+ * Server cookie is not valid but
+ * we had our cookie echoed back.
+ */
+ if (msg->cc_ok && optlen < 16)
+ ADD_STRING(target, " (echoed)");
+ /*
+ * We didn't get our cookie echoed
+ * back.
+ */
+ if (msg->cc_bad)
+ ADD_STRING(target, " (bad)");
+ ADD_STRING(target, "\n");
+ continue;
+ }
+
+ if (optcode == DNS_OPT_CLIENT_SUBNET) {
+ ADD_STRING(target, "\n");
+ continue;
+ }
+
+ /*
+ * For non-COOKIE options, add a printable
+ * version
+ */
+ ADD_STRING(target, "(\"");
+ if (isc_buffer_availablelength(target) < optlen)
+ return (ISC_R_NOSPACE);
+ for (i = 0; i < optlen; i++) {
+ 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);
+ INDENT(style);
+ 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);
+ INDENT(style);
+ 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);
+ }
+ result = ISC_R_UNEXPECTED;
+ cleanup:
+ return (result);
+}
+
+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) &&
+ (dns_master_styleflags(style) & DNS_STYLEFLAG_YAML))
+ {
+ INDENT(style);
+ ADD_STRING(target, "opcode: ");
+ ADD_STRING(target, opcodetext[msg->opcode]);
+ ADD_STRING(target, "\n");
+ INDENT(style);
+ ADD_STRING(target, "status: ");
+ result = dns_rcode_totext(msg->rcode, target);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ ADD_STRING(target, "\n");
+ INDENT(style);
+ ADD_STRING(target, "id: ");
+ snprintf(buf, sizeof(buf), "%6u", msg->id);
+ ADD_STRING(target, buf);
+ ADD_STRING(target, "\n");
+ INDENT(style);
+ ADD_STRING(target, "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");
+ ADD_STRING(target, "\n");
+ /*
+ * The final unnamed flag must be zero.
+ */
+ if ((msg->flags & 0x0040U) != 0) {
+ INDENT(style);
+ ADD_STRING(target, "MBZ: 0x4");
+ ADD_STRING(target, "\n");
+ }
+ if (msg->opcode != dns_opcode_update) {
+ INDENT(style);
+ ADD_STRING(target, "QUESTION: ");
+ } else {
+ ADD_STRING(target, "ZONE: ");
+ }
+ snprintf(buf, sizeof(buf), "%1u",
+ msg->counts[DNS_SECTION_QUESTION]);
+ ADD_STRING(target, buf);
+ ADD_STRING(target, "\n");
+ if (msg->opcode != dns_opcode_update) {
+ INDENT(style);
+ ADD_STRING(target, "ANSWER: ");
+ } else {
+ INDENT(style);
+ ADD_STRING(target, "PREREQ: ");
+ }
+ snprintf(buf, sizeof(buf), "%1u",
+ msg->counts[DNS_SECTION_ANSWER]);
+ ADD_STRING(target, buf);
+ ADD_STRING(target, "\n");
+ if (msg->opcode != dns_opcode_update) {
+ INDENT(style);
+ ADD_STRING(target, "AUTHORITY: ");
+ } else {
+ INDENT(style);
+ ADD_STRING(target, "UPDATE: ");
+ }
+ snprintf(buf, sizeof(buf), "%1u",
+ msg->counts[DNS_SECTION_AUTHORITY]);
+ ADD_STRING(target, buf);
+ ADD_STRING(target, "\n");
+ INDENT(style);
+ ADD_STRING(target, "ADDITIONAL: ");
+ snprintf(buf, sizeof(buf), "%1u",
+ msg->counts[DNS_SECTION_ADDITIONAL]);
+ ADD_STRING(target, buf);
+ ADD_STRING(target, "\n");
+ } else if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0) {
+ INDENT(style);
+ ADD_STRING(target, ";; ->>HEADER<<- opcode: ");
+ ADD_STRING(target, opcodetext[msg->opcode]);
+ ADD_STRING(target, ", status: ");
+ result = dns_rcode_totext(msg->rcode, target);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ ADD_STRING(target, ", id: ");
+ snprintf(buf, sizeof(buf), "%6u", msg->id);
+ ADD_STRING(target, buf);
+ ADD_STRING(target, "\n");
+ INDENT(style);
+ ADD_STRING(target, ";; 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");
+ /*
+ * The final unnamed flag must be zero.
+ */
+ if ((msg->flags & 0x0040U) != 0) {
+ INDENT(style);
+ ADD_STRING(target, "; MBZ: 0x4");
+ }
+ if (msg->opcode != dns_opcode_update) {
+ INDENT(style);
+ ADD_STRING(target, "; QUESTION: ");
+ } else {
+ INDENT(style);
+ 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);
+
+ cleanup:
+ return (result);
+}
+
+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);
+}
+
+void
+dns_message_logpacket(dns_message_t *message, const char *description,
+ isc_logcategory_t *category, isc_logmodule_t *module,
+ int level, isc_mem_t *mctx)
+{
+ logfmtpacket(message, description, NULL, category, module,
+ &dns_master_style_debug, level, mctx);
+}
+
+void
+dns_message_logpacket2(dns_message_t *message,
+ const char *description, isc_sockaddr_t *address,
+ isc_logcategory_t *category, isc_logmodule_t *module,
+ int level, isc_mem_t *mctx)
+{
+ REQUIRE(address != NULL);
+
+ logfmtpacket(message, description, address, category, module,
+ &dns_master_style_debug, level, mctx);
+}
+
+void
+dns_message_logfmtpacket(dns_message_t *message, const char *description,
+ isc_logcategory_t *category, isc_logmodule_t *module,
+ const dns_master_style_t *style, int level,
+ isc_mem_t *mctx)
+{
+ logfmtpacket(message, description, NULL, category, module, style,
+ level, mctx);
+}
+
+void
+dns_message_logfmtpacket2(dns_message_t *message,
+ const char *description, isc_sockaddr_t *address,
+ isc_logcategory_t *category, isc_logmodule_t *module,
+ const dns_master_style_t *style, int level,
+ isc_mem_t *mctx)
+{
+ REQUIRE(address != NULL);
+
+ logfmtpacket(message, description, address, category, module, style,
+ level, mctx);
+}
+
+static void
+logfmtpacket(dns_message_t *message, const char *description,
+ isc_sockaddr_t *address, isc_logcategory_t *category,
+ isc_logmodule_t *module, const dns_master_style_t *style,
+ int level, isc_mem_t *mctx)
+{
+ char addrbuf[ISC_SOCKADDR_FORMATSIZE] = { 0 };
+ const char *newline = "\n";
+ const char *space = " ";
+ 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.
+ */
+
+ if (address != NULL)
+ isc_sockaddr_format(address, addrbuf, sizeof(addrbuf));
+ else
+ newline = space = "";
+
+ do {
+ buf = isc_mem_get(mctx, len);
+ if (buf == NULL)
+ break;
+ isc_buffer_init(&buffer, buf, len);
+ result = dns_message_totext(message, style, 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, category, module, level,
+ "%s%s%s%s%.*s", description, space,
+ addrbuf, newline,
+ (int)isc_buffer_usedlength(&buffer),
+ buf);
+ } while (result == ISC_R_NOSPACE);
+
+ if (buf != NULL)
+ isc_mem_put(mctx, buf, len);
+}
+
+isc_result_t
+dns_message_buildopt(dns_message_t *message, dns_rdataset_t **rdatasetp,
+ unsigned int version, uint16_t udpsize,
+ unsigned int flags, dns_ednsopt_t *ednsopts, size_t count)
+{
+ dns_rdataset_t *rdataset = NULL;
+ dns_rdatalist_t *rdatalist = NULL;
+ dns_rdata_t *rdata = NULL;
+ isc_result_t result;
+ unsigned int len = 0, i;
+
+ REQUIRE(DNS_MESSAGE_VALID(message));
+ REQUIRE(rdatasetp != NULL && *rdatasetp == NULL);
+
+ result = dns_message_gettemprdatalist(message, &rdatalist);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ 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;
+
+ rdatalist->type = dns_rdatatype_opt;
+
+ /*
+ * Set Maximum UDP buffer size.
+ */
+ rdatalist->rdclass = udpsize;
+
+ /*
+ * Set EXTENDED-RCODE and Z to 0.
+ */
+ rdatalist->ttl = (version << 16);
+ rdatalist->ttl |= (flags & 0xffff);
+
+ /*
+ * Set EDNS options if applicable
+ */
+ if (count != 0U) {
+ isc_buffer_t *buf = NULL;
+ for (i = 0; i < count; i++)
+ len += ednsopts[i].length + 4;
+
+ if (len > 0xffffU) {
+ result = ISC_R_NOSPACE;
+ goto cleanup;
+ }
+
+ result = isc_buffer_allocate(message->mctx, &buf, len);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ for (i = 0; i < count; i++) {
+ isc_buffer_putuint16(buf, ednsopts[i].code);
+ isc_buffer_putuint16(buf, ednsopts[i].length);
+ if (ednsopts[i].length != 0) {
+ isc_buffer_putmem(buf, ednsopts[i].value,
+ ednsopts[i].length);
+ }
+ }
+ rdata->data = isc_buffer_base(buf);
+ rdata->length = len;
+ dns_message_takebuffer(message, &buf);
+ } else {
+ rdata->data = NULL;
+ rdata->length = 0;
+ }
+
+ rdata->rdclass = rdatalist->rdclass;
+ rdata->type = rdatalist->type;
+ rdata->flags = 0;
+
+ ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
+ result = dns_rdatalist_tordataset(rdatalist, rdataset);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ *rdatasetp = rdataset;
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (rdata != NULL)
+ dns_message_puttemprdata(message, &rdata);
+ if (rdataset != NULL)
+ dns_message_puttemprdataset(message, &rdataset);
+ if (rdatalist != NULL)
+ dns_message_puttemprdatalist(message, &rdatalist);
+ return (result);
+}
+
+void
+dns_message_setclass(dns_message_t *msg, dns_rdataclass_t rdclass) {
+
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTPARSE);
+ REQUIRE(msg->state == DNS_SECTION_ANY);
+ REQUIRE(msg->rdclass_set == 0);
+
+ msg->rdclass = rdclass;
+ msg->rdclass_set = 1;
+}
diff --git a/lib/dns/name.c b/lib/dns/name.c
new file mode 100644
index 0000000..66040fb
--- /dev/null
+++ b/lib/dns/name.c
@@ -0,0 +1,2734 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <ctype.h>
+#include <inttypes.h>
+#include <stdbool.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/random.h>
+#include <isc/string.h>
+#include <isc/thread.h>
+#include <isc/util.h>
+
+#include <dns/compress.h>
+#include <dns/fixedname.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_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_offsets) \
+ if ((name)->offsets != NULL) \
+ var = (name)->offsets; \
+ else \
+ var = (default_offsets);
+
+#define SETUP_OFFSETS(name, var, default_offsets) \
+ if ((name)->offsets != NULL) \
+ var = (name)->offsets; \
+ else { \
+ var = (default_offsets); \
+ 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[] = { "" };
+static unsigned char root_offsets[] = { 0 };
+
+static dns_name_t root = DNS_NAME_INITABSOLUTE(root_ndata, root_offsets);
+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_INITNONABSOLUTE(wild_ndata, wild_offsets);
+
+/* XXXDCL make const? */
+LIBDNS_EXTERNAL_DATA dns_name_t *dns_wildcardname = &wild;
+
+unsigned int
+dns_fullname_hash(dns_name_t *name, bool 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);
+}
+
+bool
+dns_name_isvalid(const dns_name_t *name) {
+ unsigned char *ndata, *offsets;
+ unsigned int offset, count, length, nlabels;
+
+ if (!VALID_NAME(name))
+ return (false);
+
+ if (name->length > 255U || name->labels > 127U)
+ return (false);
+
+ ndata = name->ndata;
+ length = name->length;
+ offsets = name->offsets;
+ offset = 0;
+ nlabels = 0;
+
+ while (offset != length) {
+ count = *ndata;
+ if (count > 63U)
+ return (false);
+ if (offsets != NULL && offsets[nlabels] != offset)
+ return (false);
+
+ nlabels++;
+ offset += count + 1;
+ ndata += count + 1;
+ if (offset > length)
+ return (false);
+
+ if (count == 0)
+ break;
+ }
+
+ if (nlabels != name->labels || offset != name->length)
+ return (false);
+
+ return (true);
+}
+
+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;
+}
+
+bool
+dns_name_hasbuffer(const dns_name_t *name) {
+ /*
+ * Does 'name' have a dedicated buffer?
+ */
+
+ REQUIRE(VALID_NAME(name));
+
+ if (name->buffer != NULL)
+ return (true);
+
+ return (false);
+}
+
+bool
+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 (true);
+ return (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)
+
+bool
+dns_name_ismailbox(const dns_name_t *name) {
+ unsigned char *ndata, ch;
+ unsigned int n;
+ bool first;
+
+ REQUIRE(VALID_NAME(name));
+ REQUIRE(name->labels > 0);
+ REQUIRE(name->attributes & DNS_NAMEATTR_ABSOLUTE);
+
+ /*
+ * Root label.
+ */
+ if (name->length == 1)
+ return (true);
+
+ ndata = name->ndata;
+ n = *ndata++;
+ INSIST(n <= 63);
+ while (n--) {
+ ch = *ndata++;
+ if (!domainchar(ch))
+ return (false);
+ }
+
+ if (ndata == name->ndata + name->length)
+ return (false);
+
+ /*
+ * RFC292/RFC1123 hostname.
+ */
+ while (ndata < (name->ndata + name->length)) {
+ n = *ndata++;
+ INSIST(n <= 63);
+ first = true;
+ while (n--) {
+ ch = *ndata++;
+ if (first || n == 0) {
+ if (!borderchar(ch))
+ return (false);
+ } else {
+ if (!middlechar(ch))
+ return (false);
+ }
+ first = false;
+ }
+ }
+ return (true);
+}
+
+bool
+dns_name_ishostname(const dns_name_t *name, bool wildcard) {
+ unsigned char *ndata, ch;
+ unsigned int n;
+ bool first;
+
+ REQUIRE(VALID_NAME(name));
+ REQUIRE(name->labels > 0);
+ REQUIRE(name->attributes & DNS_NAMEATTR_ABSOLUTE);
+
+ /*
+ * Root label.
+ */
+ if (name->length == 1)
+ return (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 = true;
+ while (n--) {
+ ch = *ndata++;
+ if (first || n == 0) {
+ if (!borderchar(ch))
+ return (false);
+ } else {
+ if (!middlechar(ch))
+ return (false);
+ }
+ first = false;
+ }
+ }
+ return (true);
+}
+
+bool
+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 (true);
+ }
+
+ return (false);
+}
+
+bool
+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 (true);
+ ndata += count;
+ label++;
+ }
+ return (false);
+}
+
+unsigned int
+dns_name_hash(dns_name_t *name, bool case_sensitive) {
+ unsigned int length;
+
+ /*
+ * Provide a hash value for 'name'.
+ */
+ REQUIRE(VALID_NAME(name));
+
+ if (name->labels == 0)
+ return (0);
+
+ length = name->length;
+ if (length > 16)
+ length = 16;
+
+ return (isc_hash_function_reverse(name->ndata, length,
+ case_sensitive, NULL));
+}
+
+unsigned int
+dns_name_fullhash(dns_name_t *name, bool case_sensitive) {
+ /*
+ * Provide a hash value for 'name'.
+ */
+ REQUIRE(VALID_NAME(name));
+
+ if (name->labels == 0)
+ return (0);
+
+ return (isc_hash_function_reverse(name->ndata, name->length,
+ case_sensitive, NULL));
+}
+
+unsigned int
+dns_fullname_hash(dns_name_t *name, bool 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.
+ */
+ return (dns_name_fullhash(name, case_sensitive));
+}
+
+unsigned int
+dns_name_hashbylabel(dns_name_t *name, bool 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 (isc_hash_function_reverse(name->ndata, name->length,
+ case_sensitive, NULL));
+
+ 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 += isc_hash_function_reverse(tname.ndata, tname.length,
+ case_sensitive, NULL);
+ }
+
+ 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));
+
+ if (ISC_UNLIKELY(name1 == name2)) {
+ *orderp = 0;
+ *nlabelsp = name1->labels;
+ return (dns_namereln_equal);
+ }
+
+ SETUP_OFFSETS(name1, offsets1, odata1);
+ SETUP_OFFSETS(name2, offsets2, odata2);
+
+ nlabels = 0;
+ l1 = name1->labels;
+ l2 = name2->labels;
+ if (l2 > l1) {
+ l = l1;
+ ldiff = 0 - (l2 - l1);
+ } else {
+ l = l2;
+ ldiff = l1 - l2;
+ }
+
+ offsets1 += l1;
+ offsets2 += l2;
+
+ while (ISC_LIKELY(l > 0)) {
+ l--;
+ offsets1--;
+ offsets2--;
+ label1 = &name1->ndata[*offsets1];
+ label2 = &name2->ndata[*offsets2];
+ 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;
+
+ /* Loop unrolled for performance */
+ while (ISC_LIKELY(count > 3)) {
+ chdiff = (int)maptolower[label1[0]] -
+ (int)maptolower[label2[0]];
+ if (chdiff != 0) {
+ *orderp = chdiff;
+ goto done;
+ }
+ chdiff = (int)maptolower[label1[1]] -
+ (int)maptolower[label2[1]];
+ if (chdiff != 0) {
+ *orderp = chdiff;
+ goto done;
+ }
+ chdiff = (int)maptolower[label1[2]] -
+ (int)maptolower[label2[2]];
+ if (chdiff != 0) {
+ *orderp = chdiff;
+ goto done;
+ }
+ chdiff = (int)maptolower[label1[3]] -
+ (int)maptolower[label2[3]];
+ if (chdiff != 0) {
+ *orderp = chdiff;
+ goto done;
+ }
+ count -= 4;
+ label1 += 4;
+ label2 += 4;
+ }
+ while (ISC_LIKELY(count-- > 0)) {
+ chdiff = (int)maptolower[*label1++] -
+ (int)maptolower[*label2++];
+ if (chdiff != 0) {
+ *orderp = chdiff;
+ goto done;
+ }
+ }
+ 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;
+ *nlabelsp = nlabels;
+ return (namereln);
+
+ done:
+ *nlabelsp = nlabels;
+ if (nlabels > 0)
+ 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);
+}
+
+bool
+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 (ISC_UNLIKELY(name1 == name2))
+ return (true);
+
+ if (name1->length != name2->length)
+ return (false);
+
+ l = name1->labels;
+
+ if (l != name2->labels)
+ return (false);
+
+ label1 = name1->ndata;
+ label2 = name2->ndata;
+ while (ISC_LIKELY(l-- > 0)) {
+ count = *label1++;
+ if (count != *label2++)
+ return (false);
+
+ INSIST(count <= 63); /* no bitstring support */
+
+ /* Loop unrolled for performance */
+ while (ISC_LIKELY(count > 3)) {
+ c = maptolower[label1[0]];
+ if (c != maptolower[label2[0]])
+ return (false);
+ c = maptolower[label1[1]];
+ if (c != maptolower[label2[1]])
+ return (false);
+ c = maptolower[label1[2]];
+ if (c != maptolower[label2[2]])
+ return (false);
+ c = maptolower[label1[3]];
+ if (c != maptolower[label2[3]])
+ return (false);
+ count -= 4;
+ label1 += 4;
+ label2 += 4;
+ }
+ while (ISC_LIKELY(count-- > 0)) {
+ c = maptolower[*label1++];
+ if (c != maptolower[*label2++])
+ return (false);
+ }
+ }
+
+ return (true);
+}
+
+bool
+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 (false);
+
+ if (memcmp(name1->ndata, name2->ndata, name1->length) != 0)
+ return (false);
+
+ return (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);
+}
+
+bool
+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 (true);
+
+ return (false);
+}
+
+bool
+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));
+
+#if defined(__clang__) && \
+ ( __clang_major__ < 3 || (__clang_major__ == 3 && __clang_minor__ < 2))
+ memset(&tname, 0, sizeof(tname));
+#endif
+ 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 (true);
+ return (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 *p, l;
+ unsigned int firstoffset, endoffset;
+ unsigned int i;
+
+ /*
+ * 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(n <= source->labels - first); /* note first+n could overflow */
+ REQUIRE(BINDABLE(target));
+
+ p = source->ndata;
+ if (ISC_UNLIKELY(first == source->labels)) {
+ firstoffset = source->length;
+ } else {
+ for (i = 0; i < first; i++) {
+ l = *p;
+ p += l + 1;
+ }
+ firstoffset = p - source->ndata;
+ }
+
+ if (ISC_LIKELY(first + n == source->labels))
+ endoffset = source->length;
+ else {
+ for (i = 0; i < n; i++) {
+ l = *p;
+ p += l + 1;
+ }
+ endoffset = p - source->ndata;
+ }
+
+ 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)
+ memmove(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;
+ if (len != 0)
+ memmove(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,
+ const dns_name_t *origin, unsigned int options,
+ isc_buffer_t *target)
+{
+ unsigned char *ndata, *label = NULL;
+ char *tdata;
+ char c;
+ ft_state state;
+ unsigned int value = 0, count = 0;
+ unsigned int n1 = 0, n2 = 0;
+ unsigned int tlen, nrem, nused, digits = 0, labels, tused;
+ bool done;
+ unsigned char *offsets;
+ dns_offsets_t odata;
+ bool 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 = (options & DNS_NAME_DOWNCASE);
+
+ if (target == NULL && name->buffer != NULL) {
+ target = name->buffer;
+ isc_buffer_clear(target);
+ }
+
+ REQUIRE(BINDABLE(name));
+
+ INIT_OFFSETS(name, offsets, odata);
+ offsets[0] = 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 = 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 = 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 = 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[c & 0xff];
+ *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;
+ POST(state);
+ /* FALLTHROUGH */
+ case ft_escape:
+ if (!isdigit(c & 0xff)) {
+ if (count >= 63)
+ return (DNS_R_LABELTOOLONG);
+ count++;
+ CONVERTTOASCII(c);
+ if (downcase)
+ c = maptolower[c & 0xff];
+ *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[c & 0xff];
+ 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;
+ POST(nrem);
+ 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[c & 0xff];
+ *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, 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(const dns_name_t *name, bool omit_final_dot,
+ isc_buffer_t *target)
+{
+ unsigned int options = DNS_NAME_MASTERFILE;
+
+ if (omit_final_dot)
+ options |= DNS_NAME_OMITFINALDOT;
+ return (dns_name_totext2(name, options, target));
+}
+
+isc_result_t
+dns_name_toprincipal(const dns_name_t *name, isc_buffer_t *target) {
+ return (dns_name_totext2(name, DNS_NAME_OMITFINALDOT, target));
+}
+
+isc_result_t
+dns_name_totext2(const dns_name_t *name, unsigned int options,
+ isc_buffer_t *target)
+{
+ unsigned char *ndata;
+ char *tdata;
+ unsigned int nlen, tlen;
+ unsigned char c;
+ unsigned int trem, count;
+ unsigned int labels;
+ bool saw_root = 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
+ bool omit_final_dot = (options & DNS_NAME_OMITFINALDOT);
+
+ /*
+ * 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 = true;
+ omit_final_dot = 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 = true;
+ omit_final_dot = 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 = true;
+ break;
+ }
+ if (count < 64) {
+ INSIST(nlen >= count);
+ while (count > 0) {
+ c = *ndata;
+ switch (c) {
+ /* Special modifiers in zone files. */
+ case 0x40: /* '@' */
+ case 0x24: /* '$' */
+ if ((options & DNS_NAME_MASTERFILE) == 0)
+ goto no_escape;
+ /* FALLTHROUGH */
+ case 0x22: /* '"' */
+ case 0x28: /* '(' */
+ case 0x29: /* ')' */
+ case 0x2E: /* '.' */
+ case 0x3B: /* ';' */
+ case 0x5C: /* '\\' */
+ if (trem < 2)
+ return (ISC_R_NOSPACE);
+ *tdata++ = '\\';
+ CONVERTFROMASCII(c);
+ *tdata++ = c;
+ ndata++;
+ trem -= 2;
+ nlen--;
+ break;
+ no_escape:
+ 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++;
+ tdata--;
+ }
+ if (trem > 0) {
+ *tdata = 0;
+ }
+ 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, bool 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 = 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 < 4)
+ return (ISC_R_NOSPACE);
+ snprintf(tdata, trem, "%%%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;
+ bool absolute;
+
+ ndata = name->ndata;
+ length = name->length;
+ offset = 0;
+ nlabels = 0;
+ absolute = false;
+ while (ISC_LIKELY(offset != length)) {
+ INSIST(nlabels < 128);
+ offsets[nlabels++] = offset;
+ count = *ndata;
+ INSIST(count <= 63);
+ offset += count + 1;
+ ndata += count + 1;
+ INSIST(offset <= length);
+ if (ISC_UNLIKELY(count == 0)) {
+ absolute = 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;
+ bool done;
+ fw_state state = fw_start;
+ unsigned int c;
+ unsigned char *offsets;
+ dns_offsets_t odata;
+ bool downcase;
+ bool 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 = (options & DNS_NAME_DOWNCASE);
+
+ 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 = false;
+
+ ndata = isc_buffer_used(target);
+ nused = 0;
+ seen_pointer = 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 = 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;
+ state = fw_newcurrent;
+ } else
+ return (DNS_R_BADLABELTYPE);
+ break;
+ case fw_ordinary:
+ if (downcase)
+ c = maptolower[c];
+ *ndata++ = c;
+ n--;
+ if (n == 0)
+ state = fw_start;
+ break;
+ case fw_newcurrent:
+ new_current *= 256;
+ new_current += c;
+ if (new_current >= biggest_pointer)
+ return (DNS_R_BADPOINTER);
+ biggest_pointer = new_current;
+ current = new_current;
+ cdata = (unsigned char *)source->base + current;
+ seen_pointer = 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;
+ uint16_t offset;
+ dns_name_t gp; /* Global compression prefix */
+ bool gf; /* Global compression target found */
+ 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) {
+#if defined(__clang__) && \
+ ( __clang_major__ < 3 || (__clang_major__ == 3 && __clang_minor__ < 2))
+ memset(&clname, 0, sizeof(clname));
+#endif
+ 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 = false;
+
+ /*
+ * If the offset is too high for 14 bit global compression, we're
+ * out of luck.
+ */
+ if (gf && go >= 0x4000)
+ gf = false;
+
+ /*
+ * Will the compression pointer reduce the message size?
+ */
+ if (gf && (gp.length + 2) >= name->length)
+ gf = false;
+
+ if (gf) {
+ if (target->length - target->used < gp.length)
+ return (ISC_R_NOSPACE);
+ if (gp.length != 0) {
+ unsigned char *base = target->base;
+ (void)memmove(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);
+ if (name->length != 0) {
+ unsigned char *base = target->base;
+ (void)memmove(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;
+ bool copy_prefix = true;
+ bool copy_suffix = true;
+ bool absolute = 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 = false;
+ if (suffix == NULL || suffix->labels == 0)
+ copy_suffix = false;
+ if (copy_prefix &&
+ (prefix->attributes & DNS_NAMEATTR_ABSOLUTE) != 0) {
+ absolute = 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 = true;
+ memmove(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))
+ memmove(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) &&
+ BINDABLE(prefix)));
+ REQUIRE(suffix == NULL ||
+ (VALID_NAME(suffix) &&
+ 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);
+
+ memmove(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)
+ memmove(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);
+
+ memmove(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)
+ memmove(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);
+
+#if defined(__clang__) && \
+ ( __clang_major__ < 3 || (__clang_major__ == 3 && __clang_minor__ < 2))
+ memset(&downname, 0, sizeof(downname));
+#endif
+ 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));
+}
+
+bool
+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 ?
+ true : 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, 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) {
+ if (mem != 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(const 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, 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>");
+}
+
+/*
+ * dns_name_tostring() -- similar to dns_name_format() but allocates its own
+ * memory.
+ */
+isc_result_t
+dns_name_tostring(dns_name_t *name, char **target, isc_mem_t *mctx) {
+ isc_result_t result;
+ isc_buffer_t buf;
+ isc_region_t reg;
+ char *p, txt[DNS_NAME_FORMATSIZE];
+
+ REQUIRE(VALID_NAME(name));
+ REQUIRE(target != NULL && *target == NULL);
+
+ isc_buffer_init(&buf, txt, sizeof(txt));
+ result = dns_name_totext(name, false, &buf);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ isc_buffer_usedregion(&buf, &reg);
+ p = isc_mem_allocate(mctx, reg.length + 1);
+ if (p == NULL)
+ return (ISC_R_NOMEMORY);
+ memmove(p, (char *) reg.base, (int) reg.length);
+ p[reg.length] = '\0';
+
+ *target = p;
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * dns_name_fromstring() -- convert directly from a string to a name,
+ * allocating memory as needed
+ */
+isc_result_t
+dns_name_fromstring(dns_name_t *target, const char *src, unsigned int options,
+ isc_mem_t *mctx)
+{
+ return (dns_name_fromstring2(target, src, dns_rootname, options, mctx));
+}
+
+isc_result_t
+dns_name_fromstring2(dns_name_t *target, const char *src,
+ const dns_name_t *origin, unsigned int options,
+ isc_mem_t *mctx)
+{
+ isc_result_t result;
+ isc_buffer_t buf;
+ dns_fixedname_t fn;
+ dns_name_t *name;
+
+ REQUIRE(src != NULL);
+
+ isc_buffer_constinit(&buf, src, strlen(src));
+ isc_buffer_add(&buf, strlen(src));
+ if (BINDABLE(target) && target->buffer != NULL)
+ name = target;
+ else {
+ name = dns_fixedname_initname(&fn);
+ }
+
+ result = dns_name_fromtext(name, &buf, origin, options, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ if (name != target)
+ result = dns_name_dupwithoffsets(name, mctx, target);
+ return (result);
+}
+
+isc_result_t
+dns_name_copy(const 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;
+
+ if (source->length != 0)
+ memmove(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)
+ memmove(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
+}
+
+/*
+ * Service Discovery Prefixes RFC 6763.
+ */
+static unsigned char b_dns_sd_udp_data[] = "\001b\007_dns-sd\004_udp";
+static unsigned char b_dns_sd_udp_offsets[] = { 0, 2, 10 };
+static unsigned char db_dns_sd_udp_data[] = "\002db\007_dns-sd\004_udp";
+static unsigned char db_dns_sd_udp_offsets[] = { 0, 3, 11 };
+static unsigned char r_dns_sd_udp_data[] = "\001r\007_dns-sd\004_udp";
+static unsigned char r_dns_sd_udp_offsets[] = { 0, 2, 10 };
+static unsigned char dr_dns_sd_udp_data[] = "\002dr\007_dns-sd\004_udp";
+static unsigned char dr_dns_sd_udp_offsets[] = { 0, 3, 11 };
+static unsigned char lb_dns_sd_udp_data[] = "\002lb\007_dns-sd\004_udp";
+static unsigned char lb_dns_sd_udp_offsets[] = { 0, 3, 11 };
+
+static dns_name_t const dns_sd[] = {
+ DNS_NAME_INITNONABSOLUTE(b_dns_sd_udp_data, b_dns_sd_udp_offsets),
+ DNS_NAME_INITNONABSOLUTE(db_dns_sd_udp_data, db_dns_sd_udp_offsets),
+ DNS_NAME_INITNONABSOLUTE(r_dns_sd_udp_data, r_dns_sd_udp_offsets),
+ DNS_NAME_INITNONABSOLUTE(dr_dns_sd_udp_data, dr_dns_sd_udp_offsets),
+ DNS_NAME_INITNONABSOLUTE(lb_dns_sd_udp_data, lb_dns_sd_udp_offsets)
+};
+
+bool
+dns_name_isdnssd(const dns_name_t *name) {
+ size_t i;
+ dns_name_t prefix;
+
+ if (dns_name_countlabels(name) > 3U) {
+ dns_name_init(&prefix, NULL);
+ dns_name_getlabelsequence(name, 0, 3, &prefix);
+ for (i = 0; i < (sizeof(dns_sd)/sizeof(dns_sd[0])); i++)
+ if (dns_name_equal(&prefix, &dns_sd[i]))
+ return (true);
+ }
+
+ return (false);
+}
+
+static unsigned char inaddr10_offsets[] = { 0, 3, 11, 16 };
+static unsigned char inaddr172_offsets[] = { 0, 3, 7, 15, 20 };
+static unsigned char inaddr192_offsets[] = { 0, 4, 8, 16, 21 };
+
+static unsigned char inaddr10[] = "\00210\007IN-ADDR\004ARPA";
+
+static unsigned char inaddr16172[] = "\00216\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr17172[] = "\00217\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr18172[] = "\00218\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr19172[] = "\00219\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr20172[] = "\00220\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr21172[] = "\00221\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr22172[] = "\00222\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr23172[] = "\00223\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr24172[] = "\00224\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr25172[] = "\00225\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr26172[] = "\00226\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr27172[] = "\00227\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr28172[] = "\00228\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr29172[] = "\00229\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr30172[] = "\00230\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr31172[] = "\00231\003172\007IN-ADDR\004ARPA";
+
+static unsigned char inaddr168192[] = "\003168\003192\007IN-ADDR\004ARPA";
+
+static dns_name_t const rfc1918names[] = {
+ DNS_NAME_INITABSOLUTE(inaddr10, inaddr10_offsets),
+ DNS_NAME_INITABSOLUTE(inaddr16172, inaddr172_offsets),
+ DNS_NAME_INITABSOLUTE(inaddr17172, inaddr172_offsets),
+ DNS_NAME_INITABSOLUTE(inaddr18172, inaddr172_offsets),
+ DNS_NAME_INITABSOLUTE(inaddr19172, inaddr172_offsets),
+ DNS_NAME_INITABSOLUTE(inaddr20172, inaddr172_offsets),
+ DNS_NAME_INITABSOLUTE(inaddr21172, inaddr172_offsets),
+ DNS_NAME_INITABSOLUTE(inaddr22172, inaddr172_offsets),
+ DNS_NAME_INITABSOLUTE(inaddr23172, inaddr172_offsets),
+ DNS_NAME_INITABSOLUTE(inaddr24172, inaddr172_offsets),
+ DNS_NAME_INITABSOLUTE(inaddr25172, inaddr172_offsets),
+ DNS_NAME_INITABSOLUTE(inaddr26172, inaddr172_offsets),
+ DNS_NAME_INITABSOLUTE(inaddr27172, inaddr172_offsets),
+ DNS_NAME_INITABSOLUTE(inaddr28172, inaddr172_offsets),
+ DNS_NAME_INITABSOLUTE(inaddr29172, inaddr172_offsets),
+ DNS_NAME_INITABSOLUTE(inaddr30172, inaddr172_offsets),
+ DNS_NAME_INITABSOLUTE(inaddr31172, inaddr172_offsets),
+ DNS_NAME_INITABSOLUTE(inaddr168192, inaddr192_offsets)
+};
+
+bool
+dns_name_isrfc1918(const dns_name_t *name) {
+ size_t i;
+
+ for (i = 0; i < (sizeof(rfc1918names)/sizeof(*rfc1918names)); i++)
+ if (dns_name_issubdomain(name, &rfc1918names[i]))
+ return (true);
+ return (false);
+}
+
+static unsigned char ulaoffsets[] = { 0, 2, 4, 8, 13 };
+static unsigned char ip6fc[] = "\001c\001f\003ip6\004ARPA";
+static unsigned char ip6fd[] = "\001d\001f\003ip6\004ARPA";
+
+static dns_name_t const ulanames[] = {
+ DNS_NAME_INITABSOLUTE(ip6fc, ulaoffsets),
+ DNS_NAME_INITABSOLUTE(ip6fd, ulaoffsets)
+};
+
+bool
+dns_name_isula(const dns_name_t *name) {
+ size_t i;
+
+ for (i = 0; i < (sizeof(ulanames)/sizeof(*ulanames)); i++)
+ if (dns_name_issubdomain(name, &ulanames[i]))
+ return (true);
+ return (false);
+}
+
+/*
+ * Use a simple table as we don't want all the locale stuff
+ * associated with ishexdigit().
+ */
+const char
+ishex[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
+ 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+bool
+dns_name_istat(const dns_name_t *name) {
+ unsigned char len;
+ const unsigned char *ndata;
+
+ REQUIRE(VALID_NAME(name));
+
+ if (name->labels < 1)
+ return (false);
+
+ ndata = name->ndata;
+ len = ndata[0];
+ INSIST(len <= name->length);
+ ndata++;
+
+ /*
+ * Is there at least one trust anchor reported and is the
+ * label length consistent with a trust-anchor-telemetry label.
+ */
+ if ((len < 8) || (len - 3) % 5 != 0) {
+ return (false);
+ }
+
+ if (ndata[0] != '_' ||
+ maptolower[ndata[1]] != 't' ||
+ maptolower[ndata[2]] != 'a') {
+ return (false);
+ }
+ ndata += 3;
+ len -= 3;
+
+ while (len > 0) {
+ INSIST(len >= 5);
+ if (ndata[0] != '-' || !ishex[ndata[1]] || !ishex[ndata[2]] ||
+ !ishex[ndata[3]] || !ishex[ndata[4]]) {
+ return (false);
+ }
+ ndata += 5;
+ len -= 5;
+ }
+ return (true);
+}
diff --git a/lib/dns/ncache.c b/lib/dns/ncache.c
new file mode 100644
index 0000000..c5078de
--- /dev/null
+++ b/lib/dns/ncache.c
@@ -0,0 +1,750 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.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 zero or more records of
+ * the following format:
+ *
+ * owner name
+ * type
+ * trust
+ * rdata count
+ * rdata length These two occur 'rdata count'
+ * rdata times.
+ *
+ */
+
+static isc_result_t
+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,
+ bool optout, bool secure,
+ dns_rdataset_t *addedrdataset);
+
+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, (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, (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 (addoptout(message, cache, node, covers, now, maxttl,
+ false, 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,
+ bool optout, dns_rdataset_t *addedrdataset)
+{
+ return (addoptout(message, cache, node, covers, now, maxttl,
+ optout, true, addedrdataset));
+}
+
+static isc_result_t
+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,
+ bool optout, bool secure,
+ 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.
+ */
+ dns_rdatalist_init(&ncrdatalist);
+ ncrdatalist.rdclass = dns_db_class(cache);
+ ncrdatalist.covers = covers;
+ ncrdatalist.ttl = maxttl;
+
+ /*
+ * 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 < 3)
+ return (ISC_R_NOSPACE);
+ isc_buffer_putuint16(&buffer,
+ rdataset->type);
+ isc_buffer_putuint8(&buffer,
+ (unsigned char)rdataset->trust);
+ /*
+ * 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) {
+ 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;
+ ttl = 0;
+ }
+
+ INSIST(trust != 0xffff);
+
+ ncrdatalist.ttl = ttl;
+
+ dns_rdataset_init(&ncrdataset);
+ RUNTIME_CHECK(dns_rdatalist_tordataset(&ncrdatalist, &ncrdataset)
+ == ISC_R_SUCCESS);
+ if (!secure && trust > dns_trust_answer)
+ trust = dns_trust_answer;
+ ncrdataset.trust = trust;
+ ncrdataset.attributes |= DNS_RDATASETATTR_NEGATIVE;
+ 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);
+ REQUIRE((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 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 >= 5);
+ type = isc_buffer_getuint16(&source);
+ isc_buffer_forward(&source, 1);
+ 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,
+ (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, (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 void
+rdataset_settrust(dns_rdataset_t *rdataset, dns_trust_t trust) {
+ unsigned char *raw = rdataset->private3;
+
+ raw[-1] = (unsigned char)trust;
+}
+
+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,
+ rdataset_settrust,
+ 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;
+ dns_trust_t trust = dns_trust_none;
+ dns_rdataset_t rclone;
+
+ REQUIRE(ncacherdataset != NULL);
+ REQUIRE(ncacherdataset->type == 0);
+ REQUIRE((ncacherdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0);
+ REQUIRE(name != NULL);
+ REQUIRE(!dns_rdataset_isassociated(rdataset));
+ REQUIRE(type != dns_rdatatype_rrsig);
+
+ dns_rdataset_init(&rclone);
+ dns_rdataset_clone(ncacherdataset, &rclone);
+ result = dns_rdataset_first(&rclone);
+ while (result == ISC_R_SUCCESS) {
+ dns_rdataset_current(&rclone, &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 >= 3);
+ ttype = isc_buffer_getuint16(&source);
+
+ if (ttype == type && dns_name_equal(&tname, name)) {
+ trust = isc_buffer_getuint8(&source);
+ INSIST(trust <= dns_trust_ultimate);
+ isc_buffer_remainingregion(&source, &remaining);
+ break;
+ }
+ result = dns_rdataset_next(&rclone);
+ dns_rdata_reset(&rdata);
+ }
+ dns_rdataset_disassociate(&rclone);
+ 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 = 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);
+}
+
+isc_result_t
+dns_ncache_getsigrdataset(dns_rdataset_t *ncacherdataset, dns_name_t *name,
+ dns_rdatatype_t covers, dns_rdataset_t *rdataset)
+{
+ dns_name_t tname;
+ dns_rdata_rrsig_t rrsig;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdataset_t rclone;
+ dns_rdatatype_t type;
+ dns_trust_t trust = dns_trust_none;
+ isc_buffer_t source;
+ isc_region_t remaining, sigregion;
+ isc_result_t result;
+ unsigned char *raw;
+ unsigned int count;
+
+ REQUIRE(ncacherdataset != NULL);
+ REQUIRE(ncacherdataset->type == 0);
+ REQUIRE((ncacherdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0);
+ REQUIRE(name != NULL);
+ REQUIRE(!dns_rdataset_isassociated(rdataset));
+
+ dns_rdataset_init(&rclone);
+ dns_rdataset_clone(ncacherdataset, &rclone);
+ result = dns_rdataset_first(&rclone);
+ while (result == ISC_R_SUCCESS) {
+ dns_rdataset_current(&rclone, &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);
+ isc_region_consume(&remaining, tname.length);
+
+ INSIST(remaining.length >= 2);
+ type = isc_buffer_getuint16(&source);
+ isc_region_consume(&remaining, 2);
+
+ if (type != dns_rdatatype_rrsig ||
+ !dns_name_equal(&tname, name)) {
+ result = dns_rdataset_next(&rclone);
+ dns_rdata_reset(&rdata);
+ continue;
+ }
+
+ INSIST(remaining.length >= 1);
+ trust = isc_buffer_getuint8(&source);
+ INSIST(trust <= dns_trust_ultimate);
+ isc_region_consume(&remaining, 1);
+
+ 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,
+ dns_rdatatype_rrsig, &sigregion);
+ (void)dns_rdata_tostruct(&rdata, &rrsig, NULL);
+ if (rrsig.covered == covers) {
+ isc_buffer_remainingregion(&source, &remaining);
+ break;
+ }
+
+ result = dns_rdataset_next(&rclone);
+ dns_rdata_reset(&rdata);
+ }
+ dns_rdataset_disassociate(&rclone);
+ 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 = dns_rdatatype_rrsig;
+ rdataset->covers = covers;
+ rdataset->ttl = ncacherdataset->ttl;
+ rdataset->trust = 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;
+ dns_trust_t trust;
+ 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((ncacherdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 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 >= 5);
+ type = isc_buffer_getuint16(&source);
+ trust = isc_buffer_getuint8(&source);
+ INSIST(trust <= dns_trust_ultimate);
+ 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 = 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..d0f0705
--- /dev/null
+++ b/lib/dns/nsec.c
@@ -0,0 +1,445 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdbool.h>
+
+#include <isc/log.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)
+
+void
+dns_nsec_setbit(unsigned char *array, unsigned int type, unsigned int bit) {
+ unsigned int shift, mask;
+
+ shift = 7 - (type % 8);
+ mask = 1 << shift;
+
+ if (bit != 0)
+ array[type / 8] |= mask;
+ else
+ array[type / 8] &= (~mask & 0xFF);
+}
+
+bool
+dns_nsec_isset(const unsigned char *array, unsigned int type) {
+ unsigned int byte, shift, mask;
+
+ byte = array[type / 8];
+ shift = 7 - (type % 8);
+ mask = 1 << shift;
+
+ return (byte & mask);
+}
+
+unsigned int
+dns_nsec_compressbitmap(unsigned char *map, const unsigned char *raw,
+ unsigned int max_type)
+{
+ unsigned char *start = map;
+ unsigned int window;
+ int octet;
+
+ if (raw == NULL)
+ return (0);
+
+ for (window = 0; window < 256; window++) {
+ if (window * 256 > max_type)
+ break;
+ for (octet = 31; octet >= 0; octet--)
+ if (*(raw + octet) != 0)
+ break;
+ if (octet < 0) {
+ raw += 32;
+ continue;
+ }
+ *map++ = window;
+ *map++ = octet + 1;
+ /*
+ * Note: potential overlapping move.
+ */
+ memmove(map, raw, octet + 1);
+ map += octet + 1;
+ raw += 32;
+ }
+ return (unsigned int)(map - start);
+}
+
+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;
+
+ unsigned char *nsec_bits, *bm;
+ unsigned int max_type;
+ dns_rdatasetiter_t *rdsiter;
+
+ memset(buffer, 0, DNS_NSEC_BUFFERSIZE);
+ dns_name_toregion(target, &r);
+ memmove(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;
+ dns_nsec_setbit(bm, dns_rdatatype_rrsig, 1);
+ dns_nsec_setbit(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;
+ dns_nsec_setbit(bm, rdataset.type, 1);
+ }
+ dns_rdataset_disassociate(&rdataset);
+ }
+
+ /*
+ * At zone cuts, deny the existence of glue in the parent zone.
+ */
+ if (dns_nsec_isset(bm, dns_rdatatype_ns) &&
+ ! dns_nsec_isset(bm, dns_rdatatype_soa)) {
+ for (i = 0; i <= max_type; i++) {
+ if (dns_nsec_isset(bm, i) &&
+ ! dns_rdatatype_iszonecutauth((dns_rdatatype_t)i))
+ dns_nsec_setbit(bm, i, 0);
+ }
+ }
+
+ dns_rdatasetiter_destroy(&rdsiter);
+ if (result != ISC_R_NOMORE)
+ return (result);
+
+ nsec_bits += dns_nsec_compressbitmap(nsec_bits, bm, max_type);
+
+ r.length = (unsigned int)(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));
+
+ dns_rdatalist_init(&rdatalist);
+ rdatalist.rdclass = dns_db_class(db);
+ rdatalist.type = dns_rdatatype_nsec;
+ rdatalist.ttl = ttl;
+ 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;
+
+ failure:
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ return (result);
+}
+
+bool
+dns_nsec_typepresent(dns_rdata_t *nsec, dns_rdatatype_t type) {
+ dns_rdata_nsec_t nsecstruct;
+ isc_result_t result;
+ bool 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 = 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 = dns_nsec_isset(&nsecstruct.typebits[i],
+ type % 256);
+ break;
+ }
+ dns_rdata_freestruct(&nsecstruct);
+ return (present);
+}
+
+isc_result_t
+dns_nsec_nseconly(dns_db_t *db, dns_dbversion_t *version,
+ bool *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 = false;
+ 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 = true;
+ if (result == ISC_R_NOMORE) {
+ *answer = false;
+ result = ISC_R_SUCCESS;
+ }
+ return (result);
+}
+
+/*%
+ * 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.
+ */
+isc_result_t
+dns_nsec_noexistnodata(dns_rdatatype_t type, dns_name_t *name,
+ dns_name_t *nsecname, dns_rdataset_t *nsecset,
+ bool *exists, bool *data,
+ dns_name_t *wild, dns_nseclog_t logit, void *arg)
+{
+ 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;
+ bool atparent;
+ bool ns;
+ bool 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) {
+ (*logit)(arg, ISC_LOG_DEBUG(3), "failure processing NSEC set");
+ return (result);
+ }
+ dns_rdataset_current(nsecset, &rdata);
+
+ (*logit)(arg, 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.
+ */
+ (*logit)(arg, ISC_LOG_DEBUG(3),
+ "NSEC does not cover name, before NSEC");
+ return (ISC_R_IGNORE);
+ }
+
+ if (order == 0) {
+ /*
+ * The names are the same. If we are validating "."
+ * then atparent should not be set as there is no parent.
+ */
+ atparent = (olabels != 1) && dns_rdatatype_atparent(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.
+ */
+ (*logit)(arg, 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.
+ */
+ (*logit)(arg, ISC_LOG_DEBUG(3),
+ "ignoring child nsec");
+ return (ISC_R_IGNORE);
+ }
+ if (type == dns_rdatatype_cname || type == dns_rdatatype_nxt ||
+ type == dns_rdatatype_nsec || type == dns_rdatatype_key ||
+ !dns_nsec_typepresent(&rdata, dns_rdatatype_cname)) {
+ *exists = true;
+ *data = dns_nsec_typepresent(&rdata, type);
+ (*logit)(arg, ISC_LOG_DEBUG(3),
+ "nsec proves name exists (owner) data=%d",
+ *data);
+ return (ISC_R_SUCCESS);
+ }
+ (*logit)(arg, 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.
+ */
+ (*logit)(arg, 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);
+ (*logit)(arg, 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);
+ (*logit)(arg, ISC_LOG_DEBUG(3),
+ "ignoring nsec because name is past end of range");
+ return (ISC_R_IGNORE);
+ }
+
+ if (order > 0 && relation == dns_namereln_subdomain) {
+ (*logit)(arg, ISC_LOG_DEBUG(3),
+ "nsec proves name exist (empty)");
+ dns_rdata_freestruct(&nsec);
+ *exists = true;
+ *data = 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);
+ (*logit)(arg, ISC_LOG_DEBUG(3),
+ "failure generating wildcard name");
+ return (result);
+ }
+ }
+ dns_rdata_freestruct(&nsec);
+ (*logit)(arg, ISC_LOG_DEBUG(3), "nsec range ok");
+ *exists = false;
+ return (ISC_R_SUCCESS);
+}
diff --git a/lib/dns/nsec3.c b/lib/dns/nsec3.c
new file mode 100644
index 0000000..e127893
--- /dev/null
+++ b/lib/dns/nsec3.c
@@ -0,0 +1,2113 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/base32.h>
+#include <isc/buffer.h>
+#include <isc/hex.h>
+#include <isc/iterated_hash.h>
+#include <isc/log.h>
+#include <isc/string.h>
+#include <isc/util.h>
+#include <isc/safe.h>
+
+#include <dst/dst.h>
+
+#include <dns/db.h>
+#include <dns/zone.h>
+#include <dns/compress.h>
+#include <dns/dbiterator.h>
+#include <dns/diff.h>
+#include <dns/fixedname.h>
+#include <dns/nsec.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 INITIAL(x) (((x) & DNS_NSEC3FLAG_INITIAL) != 0)
+#define REMOVE(x) (((x) & DNS_NSEC3FLAG_REMOVE) != 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;
+ bool found;
+ bool found_ns;
+ bool need_rrsig;
+
+ 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++ = (unsigned char)salt_length;
+ memmove(p, salt, salt_length);
+ p += salt_length;
+
+ *p++ = (unsigned char)hash_length;
+ memmove(p, nexthash, hash_length);
+ p += hash_length;
+
+ r.length = (unsigned int)(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 = found_ns = need_rrsig = 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;
+ dns_nsec_setbit(bm, rdataset.type, 1);
+ /*
+ * Work out if we need to set the RRSIG bit for
+ * this node. We set the RRSIG bit if either of
+ * the following conditions are met:
+ * 1) We have a SOA or DS then we need to set
+ * the RRSIG bit as both always will be signed.
+ * 2) We set the RRSIG bit if we don't have
+ * a NS record but do have other data.
+ */
+ if (rdataset.type == dns_rdatatype_soa ||
+ rdataset.type == dns_rdatatype_ds)
+ need_rrsig = true;
+ else if (rdataset.type == dns_rdatatype_ns)
+ found_ns = true;
+ else
+ found = true;
+ }
+ dns_rdataset_disassociate(&rdataset);
+ }
+ if ((found && !found_ns) || need_rrsig) {
+ if (dns_rdatatype_rrsig > max_type)
+ max_type = dns_rdatatype_rrsig;
+ dns_nsec_setbit(bm, dns_rdatatype_rrsig, 1);
+ }
+
+ /*
+ * At zone cuts, deny the existence of glue in the parent zone.
+ */
+ if (dns_nsec_isset(bm, dns_rdatatype_ns) &&
+ ! dns_nsec_isset(bm, dns_rdatatype_soa)) {
+ for (i = 0; i <= max_type; i++) {
+ if (dns_nsec_isset(bm, i) &&
+ ! dns_rdatatype_iszonecutauth((dns_rdatatype_t)i))
+ dns_nsec_setbit(bm, i, 0);
+ }
+ }
+
+ dns_rdatasetiter_destroy(&rdsiter);
+ if (result != ISC_R_NOMORE)
+ return (result);
+
+ collapse_bitmap:
+ nsec_bits += dns_nsec_compressbitmap(nsec_bits, bm, max_type);
+ r.length = (unsigned int)(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);
+}
+
+bool
+dns_nsec3_typepresent(dns_rdata_t *rdata, dns_rdatatype_t type) {
+ dns_rdata_nsec3_t nsec3;
+ isc_result_t result;
+ bool 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 = 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 = dns_nsec_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);
+
+ downcased = dns_fixedname_initname(&fixed);
+ dns_name_downcase(name, downcased, NULL);
+
+ /* hash the node name */
+ len = isc_iterated_hash(rethash, hashalg, iterations,
+ salt, (int)saltlength,
+ downcased->ndata, downcased->length);
+ if (len == 0U)
+ return (DNS_R_BADALG);
+
+ if (hash_length != NULL)
+ *hash_length = len;
+
+ /* convert the hash to base32hex non-padded */
+ region.base = rethash;
+ region.length = (unsigned int)len;
+ isc_buffer_init(&namebuffer, nametext, sizeof nametext);
+ isc_base32hexnp_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);
+}
+
+bool
+dns_nsec3_supportedhash(dns_hash_t hash) {
+ switch (hash) {
+ case dns_hash_sha1:
+ return (true);
+ }
+ return (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);
+ 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,
+ bool *exists)
+{
+ isc_result_t result;
+ dns_dbnode_t *node = NULL;
+ dns_rdatasetiter_t *iter = NULL;
+
+ result = dns_db_findnode(db, name, false, &node);
+ if (result == ISC_R_NOTFOUND) {
+ *exists = 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 = true;
+ } else if (result == ISC_R_NOMORE) {
+ *exists = false;
+ result = ISC_R_SUCCESS;
+ } else
+ *exists = false;
+ dns_rdatasetiter_destroy(&iter);
+
+ cleanup_node:
+ dns_db_detachnode(db, &node);
+ return (result);
+}
+
+static bool
+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 (true);
+ return (false);
+}
+
+/*%
+ * Delete NSEC3 records at "name" which match "param", recording the
+ * change in "diff".
+ */
+static isc_result_t
+delnsec3(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, 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);
+}
+
+static bool
+better_param(dns_rdataset_t *nsec3paramset, dns_rdata_t *param) {
+ dns_rdataset_t rdataset;
+ isc_result_t result;
+
+ if (REMOVE(param->data[1]))
+ return (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;
+ unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE];
+
+ if (rdataset.type != dns_rdatatype_nsec3param) {
+ dns_rdata_t tmprdata = DNS_RDATA_INIT;
+ dns_rdataset_current(&rdataset, &tmprdata);
+ if (!dns_nsec3param_fromprivate(&tmprdata, &rdata,
+ buf, sizeof(buf)))
+ continue;
+ } else
+ 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 (true);
+ }
+ }
+ dns_rdataset_disassociate(&rdataset);
+ return (false);
+}
+
+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, bool 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;
+ bool exists = false;
+ bool maybe_remove_unsecure = false;
+ 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;
+
+ hashname = dns_fixedname_initname(&fixed);
+ prev = dns_fixedname_initname(&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));
+ INSIST(next_length <= sizeof(nexthash));
+
+ /*
+ * 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, 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));
+ memmove(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 if (CREATE(nsec3param->flags) && OPTOUT(flags)) {
+ result = dns_nsec3_delnsec3(db, version, name,
+ nsec3param, diff);
+ goto failure;
+ } else
+ maybe_remove_unsecure = 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 (maybe_remove_unsecure) {
+ dns_rdataset_disassociate(&rdataset);
+ /*
+ * If we have OPTOUT set in the previous NSEC3 record
+ * we actually need to delete the NSEC3 record.
+ * Otherwise we just need to replace the NSEC3 record.
+ */
+ if (OPTOUT(nsec3.flags)) {
+ result = dns_nsec3_delnsec3(db, version, name,
+ nsec3param, diff);
+ goto failure;
+ }
+ goto addnsec3;
+ } 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(delnsec3(db, version, prev, nsec3param, diff));
+
+ /*
+ * Fixup the previous NSEC3.
+ */
+ nsec3.next = nexthash;
+ nsec3.next_length = (unsigned char)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));
+ memmove(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, 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(delnsec3(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, 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(delnsec3(db, version, prev, nsec3param, diff));
+
+ /*
+ * Fixup the previous NSEC3.
+ */
+ nsec3.next = nexthash;
+ nsec3.next_length = (unsigned char)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));
+ memmove(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(delnsec3(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);
+
+ /* result cannot be ISC_R_NOMORE here */
+ INSIST(result != ISC_R_NOMORE);
+
+ 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,
+ bool 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);
+ CHECK(dns_rdata_tostruct(&rdata, &nsec3param, NULL));
+
+ if (nsec3param.flags != 0)
+ continue;
+ /*
+ * 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);
+}
+
+bool
+dns_nsec3param_fromprivate(dns_rdata_t *src, dns_rdata_t *target,
+ unsigned char *buf, size_t buflen)
+{
+ dns_decompress_t dctx;
+ isc_result_t result;
+ isc_buffer_t buf1;
+ isc_buffer_t buf2;
+
+ /*
+ * Algorithm 0 (reserved by RFC 4034) is used to identify
+ * NSEC3PARAM records from DNSKEY pointers.
+ */
+ if (src->length < 1 || src->data[0] != 0)
+ return (false);
+
+ isc_buffer_init(&buf1, src->data + 1, src->length - 1);
+ isc_buffer_add(&buf1, src->length - 1);
+ isc_buffer_setactive(&buf1, src->length - 1);
+ isc_buffer_init(&buf2, buf, (unsigned int)buflen);
+ dns_decompress_init(&dctx, -1, DNS_DECOMPRESS_NONE);
+ result = dns_rdata_fromwire(target, src->rdclass,
+ dns_rdatatype_nsec3param,
+ &buf1, &dctx, 0, &buf2);
+ dns_decompress_invalidate(&dctx);
+
+ return (result == ISC_R_SUCCESS);
+}
+
+void
+dns_nsec3param_toprivate(dns_rdata_t *src, dns_rdata_t *target,
+ dns_rdatatype_t privatetype,
+ unsigned char *buf, size_t buflen)
+{
+ REQUIRE(buflen >= src->length + 1);
+
+ REQUIRE(DNS_RDATA_INITIALIZED(target));
+
+ memmove(buf + 1, src->data, src->length);
+ buf[0] = 0;
+ target->data = buf;
+ target->length = src->length + 1;
+ target->type = privatetype;
+ target->rdclass = src->rdclass;
+ target->flags = 0;
+ ISC_LINK_INIT(target, link);
+}
+
+static isc_result_t
+rr_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
+ const dns_rdata_t *rdata, bool *flag)
+{
+ dns_rdataset_t rdataset;
+ dns_dbnode_t *node = NULL;
+ isc_result_t result;
+
+ dns_rdataset_init(&rdataset);
+ if (rdata->type == dns_rdatatype_nsec3)
+ CHECK(dns_db_findnsec3node(db, name, false, &node));
+ else
+ CHECK(dns_db_findnode(db, name, false, &node));
+ result = dns_db_findrdataset(db, node, ver, rdata->type, 0,
+ (isc_stdtime_t) 0, &rdataset, NULL);
+ if (result == ISC_R_NOTFOUND) {
+ *flag = false;
+ result = ISC_R_SUCCESS;
+ goto failure;
+ }
+
+ for (result = dns_rdataset_first(&rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rdataset)) {
+ dns_rdata_t myrdata = DNS_RDATA_INIT;
+ dns_rdataset_current(&rdataset, &myrdata);
+ if (!dns_rdata_casecompare(&myrdata, rdata))
+ break;
+ }
+ dns_rdataset_disassociate(&rdataset);
+ if (result == ISC_R_SUCCESS) {
+ *flag = true;
+ } else if (result == ISC_R_NOMORE) {
+ *flag = false;
+ result = ISC_R_SUCCESS;
+ }
+
+ failure:
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ return (result);
+}
+
+isc_result_t
+dns_nsec3param_salttotext(dns_rdata_nsec3param_t *nsec3param, char *dst,
+ size_t dstlen)
+{
+ isc_result_t result;
+ isc_region_t r;
+ isc_buffer_t b;
+
+ REQUIRE(nsec3param != NULL);
+ REQUIRE(dst != NULL);
+
+ if (nsec3param->salt_length == 0) {
+ if (dstlen < 2U) {
+ return (ISC_R_NOSPACE);
+ }
+ strlcpy(dst, "-", dstlen);
+ return (ISC_R_SUCCESS);
+ }
+
+ r.base = nsec3param->salt;
+ r.length = nsec3param->salt_length;
+ isc_buffer_init(&b, dst, (unsigned int)dstlen);
+
+ result = isc_hex_totext(&r, 2, "", &b);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ if (isc_buffer_availablelength(&b) < 1) {
+ return (ISC_R_NOSPACE);
+ }
+ isc_buffer_putuint8(&b, 0);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_nsec3param_deletechains(dns_db_t *db, dns_dbversion_t *ver,
+ dns_zone_t *zone, bool nonsec,
+ dns_diff_t *diff)
+{
+ dns_dbnode_t *node = NULL;
+ dns_difftuple_t *tuple = NULL;
+ dns_name_t next;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdataset_t rdataset;
+ bool flag;
+ isc_result_t result = ISC_R_SUCCESS;
+ unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE + 1];
+ dns_name_t *origin = dns_zone_getorigin(zone);
+ dns_rdatatype_t privatetype = dns_zone_getprivatetype(zone);
+
+ dns_name_init(&next, NULL);
+ dns_rdataset_init(&rdataset);
+
+ result = dns_db_getoriginnode(db, &node);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ /*
+ * Cause all NSEC3 chains to be deleted.
+ */
+ result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3param,
+ 0, (isc_stdtime_t) 0, &rdataset, NULL);
+ if (result == ISC_R_NOTFOUND)
+ goto try_private;
+ 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 private = DNS_RDATA_INIT;
+
+ dns_rdataset_current(&rdataset, &rdata);
+
+ CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_DEL, origin,
+ rdataset.ttl, &rdata, &tuple));
+ CHECK(do_one_tuple(&tuple, db, ver, diff));
+ INSIST(tuple == NULL);
+
+ dns_nsec3param_toprivate(&rdata, &private, privatetype,
+ buf, sizeof(buf));
+ buf[2] = DNS_NSEC3FLAG_REMOVE;
+ if (nonsec)
+ buf[2] |= DNS_NSEC3FLAG_NONSEC;
+
+ CHECK(rr_exists(db, ver, origin, &private, &flag));
+
+ if (!flag) {
+ CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD,
+ origin, 0, &private,
+ &tuple));
+ CHECK(do_one_tuple(&tuple, db, ver, diff));
+ INSIST(tuple == NULL);
+ }
+ dns_rdata_reset(&rdata);
+ }
+ if (result != ISC_R_NOMORE)
+ goto failure;
+
+ dns_rdataset_disassociate(&rdataset);
+
+ try_private:
+ if (privatetype == 0)
+ goto success;
+ result = dns_db_findrdataset(db, node, ver, privatetype, 0,
+ (isc_stdtime_t) 0, &rdataset, NULL);
+ if (result == ISC_R_NOTFOUND)
+ goto 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_reset(&rdata);
+ dns_rdataset_current(&rdataset, &rdata);
+ INSIST(rdata.length <= sizeof(buf));
+ memmove(buf, rdata.data, rdata.length);
+
+ /*
+ * Private NSEC3 record length >= 6.
+ * <0(1), hash(1), flags(1), iterations(2), saltlen(1)>
+ */
+ if (rdata.length < 6 || buf[0] != 0 ||
+ (buf[2] & DNS_NSEC3FLAG_REMOVE) != 0 ||
+ (nonsec && (buf[2] & DNS_NSEC3FLAG_NONSEC) != 0))
+ continue;
+
+ CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_DEL, origin,
+ 0, &rdata, &tuple));
+ CHECK(do_one_tuple(&tuple, db, ver, diff));
+ INSIST(tuple == NULL);
+
+ rdata.data = buf;
+ buf[2] = DNS_NSEC3FLAG_REMOVE;
+ if (nonsec)
+ buf[2] |= DNS_NSEC3FLAG_NONSEC;
+
+ CHECK(rr_exists(db, ver, origin, &rdata, &flag));
+
+ if (!flag) {
+ CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD,
+ origin, 0, &rdata, &tuple));
+ CHECK(do_one_tuple(&tuple, db, ver, diff));
+ INSIST(tuple == NULL);
+ }
+ }
+ if (result != ISC_R_NOMORE)
+ goto failure;
+ success:
+ result = ISC_R_SUCCESS;
+
+ failure:
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ dns_db_detachnode(db, &node);
+ return (result);
+}
+
+isc_result_t
+dns_nsec3_addnsec3sx(dns_db_t *db, dns_dbversion_t *version,
+ dns_name_t *name, dns_ttl_t nsecttl,
+ bool unsecure, dns_rdatatype_t type,
+ dns_diff_t *diff)
+{
+ dns_dbnode_t *node = NULL;
+ dns_rdata_nsec3param_t nsec3param;
+ dns_rdataset_t rdataset;
+ dns_rdataset_t prdataset;
+ isc_result_t result;
+
+ dns_rdataset_init(&rdataset);
+ dns_rdataset_init(&prdataset);
+
+ /*
+ * 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, type, 0, 0,
+ &prdataset, NULL);
+ if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND)
+ goto failure;
+
+ result = dns_db_findrdataset(db, node, version,
+ dns_rdatatype_nsec3param, 0, 0,
+ &rdataset, NULL);
+ if (result == ISC_R_NOTFOUND)
+ goto try_private;
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ /*
+ * 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);
+ CHECK(dns_rdata_tostruct(&rdata, &nsec3param, NULL));
+
+ if (nsec3param.flags != 0)
+ continue;
+
+ /*
+ * We have a active chain. Update it.
+ */
+ CHECK(dns_nsec3_addnsec3(db, version, name, &nsec3param,
+ nsecttl, unsecure, diff));
+ }
+ if (result != ISC_R_NOMORE)
+ goto failure;
+
+ dns_rdataset_disassociate(&rdataset);
+
+ try_private:
+ if (!dns_rdataset_isassociated(&prdataset))
+ goto success;
+ /*
+ * Update each active NSEC3 chain.
+ */
+ for (result = dns_rdataset_first(&prdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&prdataset)) {
+ dns_rdata_t rdata1 = DNS_RDATA_INIT;
+ dns_rdata_t rdata2 = DNS_RDATA_INIT;
+ unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE];
+
+ dns_rdataset_current(&prdataset, &rdata1);
+ if (!dns_nsec3param_fromprivate(&rdata1, &rdata2,
+ buf, sizeof(buf)))
+ continue;
+ CHECK(dns_rdata_tostruct(&rdata2, &nsec3param, NULL));
+
+ if ((nsec3param.flags & DNS_NSEC3FLAG_REMOVE) != 0)
+ continue;
+ if (better_param(&prdataset, &rdata2))
+ continue;
+
+ /*
+ * We have a active chain. Update it.
+ */
+ CHECK(dns_nsec3_addnsec3(db, version, name, &nsec3param,
+ nsecttl, unsecure, diff));
+ }
+ if (result == ISC_R_NOMORE)
+ success:
+ result = ISC_R_SUCCESS;
+ failure:
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ if (dns_rdataset_isassociated(&prdataset))
+ dns_rdataset_disassociate(&prdataset);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+
+ return (result);
+}
+
+/*%
+ * Determine whether any NSEC3 records that were associated with
+ * 'name' should be deleted or if they should continue to exist.
+ * true indicates they should be deleted.
+ * false indicates they should be retained.
+ */
+static isc_result_t
+deleteit(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
+ bool *yesno)
+{
+ isc_result_t result;
+ dns_fixedname_t foundname;
+ dns_fixedname_init(&foundname);
+
+ result = dns_db_find(db, name, ver, dns_rdatatype_any,
+ DNS_DBFIND_GLUEOK | DNS_DBFIND_NOWILD,
+ (isc_stdtime_t) 0, NULL,
+ dns_fixedname_name(&foundname),
+ NULL, NULL);
+ if (result == DNS_R_EMPTYNAME || result == ISC_R_SUCCESS ||
+ result == DNS_R_ZONECUT) {
+ *yesno = false;
+ return (ISC_R_SUCCESS);
+ }
+ if (result == DNS_R_GLUE || result == DNS_R_DNAME ||
+ result == DNS_R_DELEGATION || result == DNS_R_NXDOMAIN) {
+ *yesno = true;
+ return (ISC_R_SUCCESS);
+ }
+ /*
+ * Silence compiler.
+ */
+ *yesno = true;
+ 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;
+ bool yesno;
+ 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;
+
+ hashname = dns_fixedname_initname(&fixed);
+ prev = dns_fixedname_initname(&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 || result == DNS_R_PARTIALMATCH)
+ 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));
+ memmove(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(delnsec3(db, version, prev, nsec3param, diff));
+
+ /*
+ * Fixup the previous NSEC3.
+ */
+ nsec3.next = nexthash;
+ nsec3.next_length = (unsigned char)next_length;
+ if (CREATE(nsec3param->flags))
+ nsec3.flags = nsec3param->flags & DNS_NSEC3FLAG_OPTOUT;
+ 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(delnsec3(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(deleteit(db, version, &empty, &yesno));
+ if (!yesno)
+ 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 || result == DNS_R_PARTIALMATCH)
+ 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));
+ memmove(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(delnsec3(db, version, prev, nsec3param, diff));
+
+ /*
+ * Fixup the previous NSEC3.
+ */
+ nsec3.next = nexthash;
+ nsec3.next_length = (unsigned char)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(delnsec3(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)
+{
+ return (dns_nsec3_delnsec3sx(db, version, name, 0, diff));
+}
+
+isc_result_t
+dns_nsec3_delnsec3sx(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name,
+ dns_rdatatype_t privatetype, 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);
+ if (result == ISC_R_NOTFOUND)
+ goto try_private;
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ /*
+ * 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);
+ CHECK(dns_rdata_tostruct(&rdata, &nsec3param, NULL));
+
+ if (nsec3param.flags != 0)
+ continue;
+ /*
+ * We have a active chain. Update it.
+ */
+ CHECK(dns_nsec3_delnsec3(db, version, name, &nsec3param, diff));
+ }
+ dns_rdataset_disassociate(&rdataset);
+
+ try_private:
+ if (privatetype == 0)
+ goto success;
+ result = dns_db_findrdataset(db, node, version, privatetype, 0, 0,
+ &rdataset, NULL);
+ if (result == ISC_R_NOTFOUND)
+ goto success;
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ /*
+ * Update each NSEC3 chain being built.
+ */
+ for (result = dns_rdataset_first(&rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rdataset)) {
+ dns_rdata_t rdata1 = DNS_RDATA_INIT;
+ dns_rdata_t rdata2 = DNS_RDATA_INIT;
+ unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE];
+
+ dns_rdataset_current(&rdataset, &rdata1);
+ if (!dns_nsec3param_fromprivate(&rdata1, &rdata2,
+ buf, sizeof(buf)))
+ continue;
+ CHECK(dns_rdata_tostruct(&rdata2, &nsec3param, NULL));
+
+ if ((nsec3param.flags & DNS_NSEC3FLAG_REMOVE) != 0)
+ continue;
+ if (better_param(&rdataset, &rdata2))
+ continue;
+
+ /*
+ * We have a active chain. Update it.
+ */
+ CHECK(dns_nsec3_delnsec3(db, version, name, &nsec3param, diff));
+ }
+ if (result == ISC_R_NOMORE)
+ success:
+ 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,
+ bool complete, bool *answer)
+{
+ return (dns_nsec3_activex(db, version, complete, 0, answer));
+}
+
+isc_result_t
+dns_nsec3_activex(dns_db_t *db, dns_dbversion_t *version,
+ bool complete, dns_rdatatype_t privatetype,
+ bool *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);
+
+ if (result == ISC_R_NOTFOUND)
+ goto try_private;
+
+ 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_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)
+ break;
+ }
+ dns_rdataset_disassociate(&rdataset);
+ if (result == ISC_R_SUCCESS) {
+ dns_db_detachnode(db, &node);
+ *answer = true;
+ return (ISC_R_SUCCESS);
+ }
+ if (result == ISC_R_NOMORE)
+ *answer = false;
+
+ try_private:
+ if (privatetype == 0 || complete) {
+ *answer = false;
+ return (ISC_R_SUCCESS);
+ }
+ result = dns_db_findrdataset(db, node, version, privatetype, 0, 0,
+ &rdataset, NULL);
+
+ dns_db_detachnode(db, &node);
+ if (result == ISC_R_NOTFOUND) {
+ *answer = 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 rdata1 = DNS_RDATA_INIT;
+ dns_rdata_t rdata2 = DNS_RDATA_INIT;
+ unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE];
+
+ dns_rdataset_current(&rdataset, &rdata1);
+ if (!dns_nsec3param_fromprivate(&rdata1, &rdata2,
+ buf, sizeof(buf)))
+ continue;
+ result = dns_rdata_tostruct(&rdata2, &nsec3param, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ if (!complete && CREATE(nsec3param.flags))
+ break;
+ }
+ dns_rdataset_disassociate(&rdataset);
+ if (result == ISC_R_SUCCESS) {
+ *answer = true;
+ result = ISC_R_SUCCESS;
+ }
+ if (result == ISC_R_NOMORE) {
+ *answer = 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;
+ unsigned int 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_size(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);
+}
+
+isc_result_t
+dns_nsec3_noexistnodata(dns_rdatatype_t type, dns_name_t* name,
+ dns_name_t *nsec3name, dns_rdataset_t *nsec3set,
+ dns_name_t *zonename, bool *exists,
+ bool *data, bool *optout,
+ bool *unknown, bool *setclosest,
+ bool *setnearest, dns_name_t *closest,
+ dns_name_t *nearest, dns_nseclog_t logit, void *arg)
+{
+ 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;
+ bool atparent;
+ bool first;
+ bool ns;
+ bool 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) {
+ (*logit)(arg, 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);
+
+ (*logit)(arg, ISC_LOG_DEBUG(3), "looking for relevant NSEC3");
+
+ zone = dns_fixedname_initname(&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 = 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 = isc_safe_memcompare(owner, nsec3.next, nsec3.next_length);
+
+ /*
+ * Prepare to compute all the hashes.
+ */
+ qname = dns_fixedname_initname(&qfixed);
+ dns_name_downcase(name, qname, NULL);
+ qlabels = dns_name_countlabels(qname);
+ first = 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) {
+ (*logit)(arg, ISC_LOG_DEBUG(3),
+ "ignoring NSEC bad length %u vs %u",
+ length, nsec3.next_length);
+ return (ISC_R_IGNORE);
+ }
+
+ order = isc_safe_memcompare(hash, owner, length);
+ if (first && order == 0) {
+ /*
+ * The hashes are the same.
+ */
+ atparent = dns_rdatatype_atparent(type);
+ ns = dns_nsec3_typepresent(&rdata, dns_rdatatype_ns);
+ soa = dns_nsec3_typepresent(&rdata, dns_rdatatype_soa);
+ if (ns && !soa) {
+ if (!atparent) {
+ /*
+ * This NSEC3 record is from somewhere
+ * higher in the DNS, and at the
+ * parent of a delegation. It can not
+ * be legitimately used here.
+ */
+ (*logit)(arg, ISC_LOG_DEBUG(3),
+ "ignoring parent NSEC3");
+ return (ISC_R_IGNORE);
+ }
+ } else if (atparent && ns && soa) {
+ /*
+ * This NSEC3 record is from the child.
+ * It can not be legitimately used here.
+ */
+ (*logit)(arg, ISC_LOG_DEBUG(3),
+ "ignoring child NSEC3");
+ return (ISC_R_IGNORE);
+ }
+ if (type == dns_rdatatype_cname ||
+ type == dns_rdatatype_nxt ||
+ type == dns_rdatatype_nsec ||
+ type == dns_rdatatype_key ||
+ !dns_nsec3_typepresent(&rdata, dns_rdatatype_cname)) {
+ *exists = true;
+ *data = dns_nsec3_typepresent(&rdata, type);
+ (*logit)(arg, ISC_LOG_DEBUG(3),
+ "NSEC3 proves name exists (owner) "
+ "data=%d", *data);
+ return (ISC_R_SUCCESS);
+ }
+ (*logit)(arg, 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.
+ */
+ (*logit)(arg, 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));
+ (*logit)(arg, ISC_LOG_DEBUG(3),
+ "NSEC3 indicates potential closest "
+ "encloser: '%s'", namebuf);
+ dns_name_copy(qname, closest, NULL);
+ *setclosest = true;
+ }
+ dns_name_format(qname, namebuf, sizeof(namebuf));
+ (*logit)(arg, 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-existence 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)))
+ {
+ dns_name_format(qname, namebuf, sizeof(namebuf));
+ (*logit)(arg, 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 = true;
+ }
+
+ *exists = false;
+ *data = false;
+ if (optout != NULL) {
+ if ((nsec3.flags & DNS_NSEC3FLAG_OPTOUT) != 0)
+ (*logit)(arg, ISC_LOG_DEBUG(3),
+ "NSEC3 indicates optout");
+ else
+ (*logit)(arg, ISC_LOG_DEBUG(3),
+ "NSEC3 indicates secure range");
+ *optout = (nsec3.flags & DNS_NSEC3FLAG_OPTOUT);
+ }
+ answer = ISC_R_SUCCESS;
+ }
+
+ qlabels--;
+ if (qlabels > 0)
+ dns_name_split(qname, qlabels, NULL, qname);
+ first = false;
+ }
+ return (answer);
+}
diff --git a/lib/dns/nta.c b/lib/dns/nta.c
new file mode 100644
index 0000000..41e219b
--- /dev/null
+++ b/lib/dns/nta.c
@@ -0,0 +1,722 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/buffer.h>
+#include <isc/log.h>
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/rwlock.h>
+#include <isc/string.h>
+#include <isc/task.h>
+#include <isc/time.h>
+#include <isc/timer.h>
+#include <isc/util.h>
+
+#include <dns/db.h>
+#include <dns/log.h>
+#include <dns/nta.h>
+#include <dns/fixedname.h>
+#include <dns/name.h>
+#include <dns/rbt.h>
+#include <dns/rdataset.h>
+#include <dns/resolver.h>
+#include <dns/result.h>
+#include <dns/time.h>
+
+struct dns_nta {
+ unsigned int magic;
+ isc_refcount_t refcount;
+ dns_ntatable_t *ntatable;
+ bool forced;
+ isc_timer_t *timer;
+ dns_fetch_t *fetch;
+ dns_rdataset_t rdataset;
+ dns_rdataset_t sigrdataset;
+ dns_fixedname_t fn;
+ dns_name_t *name;
+ isc_stdtime_t expiry;
+};
+
+#define NTA_MAGIC ISC_MAGIC('N', 'T', 'A', 'n')
+#define VALID_NTA(nn) ISC_MAGIC_VALID(nn, NTA_MAGIC)
+
+/*
+ * Obtain a reference to the nta object. Released by
+ * nta_detach.
+ */
+static void
+nta_ref(dns_nta_t *nta) {
+ isc_refcount_increment(&nta->refcount, NULL);
+}
+
+static void
+nta_detach(isc_mem_t *mctx, dns_nta_t **ntap) {
+ unsigned int refs;
+ dns_nta_t *nta = *ntap;
+
+ REQUIRE(VALID_NTA(nta));
+
+ *ntap = NULL;
+ isc_refcount_decrement(&nta->refcount, &refs);
+ if (refs == 0) {
+ nta->magic = 0;
+ if (nta->timer != NULL) {
+ (void) isc_timer_reset(nta->timer,
+ isc_timertype_inactive,
+ NULL, NULL, true);
+ isc_timer_detach(&nta->timer);
+ }
+ isc_refcount_destroy(&nta->refcount);
+ if (dns_rdataset_isassociated(&nta->rdataset))
+ dns_rdataset_disassociate(&nta->rdataset);
+ if (dns_rdataset_isassociated(&nta->sigrdataset))
+ dns_rdataset_disassociate(&nta->sigrdataset);
+ if (nta->fetch != NULL) {
+ dns_resolver_cancelfetch(nta->fetch);
+ dns_resolver_destroyfetch(&nta->fetch);
+ }
+ isc_mem_put(mctx, nta, sizeof(dns_nta_t));
+ }
+}
+
+static void
+free_nta(void *data, void *arg) {
+ dns_nta_t *nta = (dns_nta_t *) data;
+ isc_mem_t *mctx = (isc_mem_t *) arg;
+
+ nta_detach(mctx, &nta);
+}
+
+isc_result_t
+dns_ntatable_create(dns_view_t *view,
+ isc_taskmgr_t *taskmgr, isc_timermgr_t *timermgr,
+ dns_ntatable_t **ntatablep)
+{
+ dns_ntatable_t *ntatable;
+ isc_result_t result;
+
+ REQUIRE(ntatablep != NULL && *ntatablep == NULL);
+
+ ntatable = isc_mem_get(view->mctx, sizeof(*ntatable));
+ if (ntatable == NULL)
+ return (ISC_R_NOMEMORY);
+
+ ntatable->task = NULL;
+ result = isc_task_create(taskmgr, 0, &ntatable->task);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_ntatable;
+ isc_task_setname(ntatable->task, "ntatable", ntatable);
+
+ ntatable->table = NULL;
+ result = dns_rbt_create(view->mctx, free_nta, view->mctx,
+ &ntatable->table);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_task;
+
+ result = isc_rwlock_init(&ntatable->rwlock, 0, 0);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_rbt;
+
+ ntatable->timermgr = timermgr;
+ ntatable->taskmgr = taskmgr;
+
+ ntatable->view = view;
+ ntatable->references = 1;
+
+ ntatable->magic = NTATABLE_MAGIC;
+ *ntatablep = ntatable;
+
+ return (ISC_R_SUCCESS);
+
+ cleanup_rbt:
+ dns_rbt_destroy(&ntatable->table);
+
+ cleanup_task:
+ isc_task_detach(&ntatable->task);
+
+ cleanup_ntatable:
+ isc_mem_put(ntatable->view->mctx, ntatable, sizeof(*ntatable));
+
+ return (result);
+}
+
+void
+dns_ntatable_attach(dns_ntatable_t *source, dns_ntatable_t **targetp) {
+ REQUIRE(VALID_NTATABLE(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_ntatable_detach(dns_ntatable_t **ntatablep) {
+ bool destroy = false;
+ dns_ntatable_t *ntatable;
+
+ REQUIRE(ntatablep != NULL && VALID_NTATABLE(*ntatablep));
+
+ ntatable = *ntatablep;
+ *ntatablep = NULL;
+
+ RWLOCK(&ntatable->rwlock, isc_rwlocktype_write);
+ INSIST(ntatable->references > 0);
+ ntatable->references--;
+ if (ntatable->references == 0)
+ destroy = true;
+ RWUNLOCK(&ntatable->rwlock, isc_rwlocktype_write);
+
+ if (destroy) {
+ dns_rbt_destroy(&ntatable->table);
+ isc_rwlock_destroy(&ntatable->rwlock);
+ if (ntatable->task != NULL)
+ isc_task_detach(&ntatable->task);
+ ntatable->timermgr = NULL;
+ ntatable->taskmgr = NULL;
+ ntatable->magic = 0;
+ isc_mem_put(ntatable->view->mctx, ntatable, sizeof(*ntatable));
+ }
+}
+
+static void
+fetch_done(isc_task_t *task, isc_event_t *event) {
+ dns_fetchevent_t *devent = (dns_fetchevent_t *)event;
+ dns_nta_t *nta = devent->ev_arg;
+ isc_result_t eresult = devent->result;
+ dns_ntatable_t *ntatable = nta->ntatable;
+ dns_view_t *view = ntatable->view;
+ isc_stdtime_t now;
+
+ UNUSED(task);
+
+ if (dns_rdataset_isassociated(&nta->rdataset))
+ dns_rdataset_disassociate(&nta->rdataset);
+ if (dns_rdataset_isassociated(&nta->sigrdataset))
+ dns_rdataset_disassociate(&nta->sigrdataset);
+ if (nta->fetch == devent->fetch)
+ nta->fetch = NULL;
+ dns_resolver_destroyfetch(&devent->fetch);
+
+ if (devent->node != NULL)
+ dns_db_detachnode(devent->db, &devent->node);
+ if (devent->db != NULL)
+ dns_db_detach(&devent->db);
+
+ isc_event_free(&event);
+ isc_stdtime_get(&now);
+
+ switch (eresult) {
+ case ISC_R_SUCCESS:
+ case DNS_R_NCACHENXDOMAIN:
+ case DNS_R_NXDOMAIN:
+ case DNS_R_NCACHENXRRSET:
+ case DNS_R_NXRRSET:
+ if (nta->expiry > now)
+ nta->expiry = now;
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * If we're expiring before the next recheck, we might
+ * as well stop the timer now.
+ */
+ if (nta->timer != NULL && nta->expiry - now < view->nta_recheck)
+ (void) isc_timer_reset(nta->timer, isc_timertype_inactive,
+ NULL, NULL, true);
+ nta_detach(view->mctx, &nta);
+}
+
+static void
+checkbogus(isc_task_t *task, isc_event_t *event) {
+ dns_nta_t *nta = event->ev_arg;
+ dns_ntatable_t *ntatable = nta->ntatable;
+ dns_view_t *view = ntatable->view;
+ isc_result_t result;
+
+ if (nta->fetch != NULL) {
+ dns_resolver_cancelfetch(nta->fetch);
+ nta->fetch = NULL;
+ }
+ if (dns_rdataset_isassociated(&nta->rdataset))
+ dns_rdataset_disassociate(&nta->rdataset);
+ if (dns_rdataset_isassociated(&nta->sigrdataset))
+ dns_rdataset_disassociate(&nta->sigrdataset);
+
+ isc_event_free(&event);
+
+ nta_ref(nta);
+ result = dns_resolver_createfetch(view->resolver, nta->name,
+ dns_rdatatype_nsec,
+ NULL, NULL, NULL,
+ DNS_FETCHOPT_NONTA,
+ task, fetch_done, nta,
+ &nta->rdataset,
+ &nta->sigrdataset,
+ &nta->fetch);
+ if (result != ISC_R_SUCCESS)
+ nta_detach(view->mctx, &nta);
+}
+
+static isc_result_t
+settimer(dns_ntatable_t *ntatable, dns_nta_t *nta, uint32_t lifetime) {
+ isc_result_t result;
+ isc_interval_t interval;
+ dns_view_t *view;
+
+ REQUIRE(VALID_NTATABLE(ntatable));
+ REQUIRE(VALID_NTA(nta));
+
+ if (ntatable->timermgr == NULL)
+ return (ISC_R_SUCCESS);
+
+ view = ntatable->view;
+ if (view->nta_recheck == 0 || lifetime <= view->nta_recheck)
+ return (ISC_R_SUCCESS);
+
+ isc_interval_set(&interval, view->nta_recheck, 0);
+ result = isc_timer_create(ntatable->timermgr, isc_timertype_ticker,
+ NULL, &interval, ntatable->task,
+ checkbogus, nta, &nta->timer);
+ return (result);
+}
+
+static isc_result_t
+nta_create(dns_ntatable_t *ntatable, dns_name_t *name, dns_nta_t **target) {
+ isc_result_t result;
+ dns_nta_t *nta = NULL;
+ dns_view_t *view;
+
+ REQUIRE(VALID_NTATABLE(ntatable));
+ REQUIRE(target != NULL && *target == NULL);
+
+ view = ntatable->view;
+
+ nta = isc_mem_get(view->mctx, sizeof(dns_nta_t));
+ if (nta == NULL)
+ return (ISC_R_NOMEMORY);
+
+ nta->ntatable = ntatable;
+ nta->expiry = 0;
+ nta->timer = NULL;
+ nta->fetch = NULL;
+ dns_rdataset_init(&nta->rdataset);
+ dns_rdataset_init(&nta->sigrdataset);
+
+ result = isc_refcount_init(&nta->refcount, 1);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(view->mctx, nta, sizeof(dns_nta_t));
+ return (result);
+ }
+
+ nta->name = dns_fixedname_initname(&nta->fn);
+ dns_name_copy(name, nta->name, NULL);
+
+ nta->magic = NTA_MAGIC;
+
+ *target = nta;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_ntatable_add(dns_ntatable_t *ntatable, dns_name_t *name,
+ bool force, isc_stdtime_t now,
+ uint32_t lifetime)
+{
+ isc_result_t result;
+ dns_nta_t *nta = NULL;
+ dns_rbtnode_t *node;
+ dns_view_t *view;
+
+ REQUIRE(VALID_NTATABLE(ntatable));
+
+ view = ntatable->view;
+
+ result = nta_create(ntatable, name, &nta);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ nta->expiry = now + lifetime;
+ nta->forced = force;
+
+ RWLOCK(&ntatable->rwlock, isc_rwlocktype_write);
+
+ node = NULL;
+ result = dns_rbt_addnode(ntatable->table, name, &node);
+ if (result == ISC_R_SUCCESS) {
+ if (!force)
+ (void)settimer(ntatable, nta, lifetime);
+ node->data = nta;
+ nta = NULL;
+ } else if (result == ISC_R_EXISTS) {
+ dns_nta_t *n = node->data;
+ if (n == NULL) {
+ if (!force)
+ (void)settimer(ntatable, nta, lifetime);
+ node->data = nta;
+ nta = NULL;
+ } else {
+ n->expiry = nta->expiry;
+ nta_detach(view->mctx, &nta);
+ }
+ result = ISC_R_SUCCESS;
+ }
+
+ RWUNLOCK(&ntatable->rwlock, isc_rwlocktype_write);
+
+ if (nta != NULL)
+ nta_detach(view->mctx, &nta);
+
+ return (result);
+}
+
+/*
+ * Caller must hold a write lock on rwlock.
+ */
+static isc_result_t
+deletenode(dns_ntatable_t *ntatable, const dns_name_t *name) {
+ isc_result_t result;
+ dns_rbtnode_t *node = NULL;
+
+ REQUIRE(VALID_NTATABLE(ntatable));
+ REQUIRE(name != NULL);
+
+ result = dns_rbt_findnode(ntatable->table, name, NULL, &node, NULL,
+ DNS_RBTFIND_NOOPTIONS, NULL, NULL);
+ if (result == ISC_R_SUCCESS) {
+ if (node->data != NULL)
+ result = dns_rbt_deletenode(ntatable->table,
+ node, false);
+ else
+ result = ISC_R_NOTFOUND;
+ } else if (result == DNS_R_PARTIALMATCH)
+ result = ISC_R_NOTFOUND;
+
+ return (result);
+}
+
+isc_result_t
+dns_ntatable_delete(dns_ntatable_t *ntatable, dns_name_t *name) {
+ isc_result_t result;
+
+ RWLOCK(&ntatable->rwlock, isc_rwlocktype_write);
+ result = deletenode(ntatable, name);
+ RWUNLOCK(&ntatable->rwlock, isc_rwlocktype_write);
+
+ return (result);
+}
+
+bool
+dns_ntatable_covered(dns_ntatable_t *ntatable, isc_stdtime_t now,
+ dns_name_t *name, dns_name_t *anchor)
+{
+ isc_result_t result;
+ dns_fixedname_t fn;
+ dns_rbtnode_t *node;
+ dns_name_t *foundname;
+ dns_nta_t *nta = NULL;
+ bool answer = false;
+ isc_rwlocktype_t locktype = isc_rwlocktype_read;
+
+ REQUIRE(ntatable == NULL || VALID_NTATABLE(ntatable));
+ REQUIRE(dns_name_isabsolute(name));
+
+ if (ntatable == NULL)
+ return (false);
+
+ foundname = dns_fixedname_initname(&fn);
+
+ relock:
+ RWLOCK(&ntatable->rwlock, locktype);
+ again:
+ node = NULL;
+ result = dns_rbt_findnode(ntatable->table, name, foundname, &node, NULL,
+ DNS_RBTFIND_NOOPTIONS, NULL, NULL);
+ if (result == DNS_R_PARTIALMATCH) {
+ if (dns_name_issubdomain(foundname, anchor))
+ result = ISC_R_SUCCESS;
+ }
+ if (result == ISC_R_SUCCESS) {
+ nta = (dns_nta_t *) node->data;
+ answer = (nta->expiry > now);
+ }
+
+ /* Deal with expired NTA */
+ if (result == ISC_R_SUCCESS && !answer) {
+ char nb[DNS_NAME_FORMATSIZE];
+
+ if (locktype == isc_rwlocktype_read) {
+ RWUNLOCK(&ntatable->rwlock, locktype);
+ locktype = isc_rwlocktype_write;
+ goto relock;
+ }
+
+ dns_name_format(foundname, nb, sizeof(nb));
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC,
+ DNS_LOGMODULE_NTA, ISC_LOG_INFO,
+ "deleting expired NTA at %s", nb);
+
+ if (nta->timer != NULL) {
+ (void) isc_timer_reset(nta->timer,
+ isc_timertype_inactive,
+ NULL, NULL, true);
+ isc_timer_detach(&nta->timer);
+ }
+
+ result = deletenode(ntatable, foundname);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC,
+ DNS_LOGMODULE_NTA, ISC_LOG_INFO,
+ "deleting NTA failed: %s",
+ isc_result_totext(result));
+ }
+ goto again;
+ }
+ RWUNLOCK(&ntatable->rwlock, locktype);
+
+ return (answer);
+}
+
+static isc_result_t
+putstr(isc_buffer_t **b, const char *str) {
+ isc_result_t result;
+
+ result = isc_buffer_reserve(b, strlen(str));
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ isc_buffer_putstr(*b, str);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_ntatable_totext(dns_ntatable_t *ntatable, isc_buffer_t **buf) {
+ isc_result_t result;
+ dns_rbtnode_t *node;
+ dns_rbtnodechain_t chain;
+ bool first = true;
+ isc_stdtime_t now;
+
+ REQUIRE(VALID_NTATABLE(ntatable));
+
+ isc_stdtime_get(&now);
+
+ RWLOCK(&ntatable->rwlock, isc_rwlocktype_read);
+ dns_rbtnodechain_init(&chain, ntatable->view->mctx);
+ result = dns_rbtnodechain_first(&chain, ntatable->table, NULL, NULL);
+ if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
+ if (result == ISC_R_NOTFOUND)
+ result = ISC_R_SUCCESS;
+ goto cleanup;
+ }
+ for (;;) {
+ dns_rbtnodechain_current(&chain, NULL, NULL, &node);
+ if (node->data != NULL) {
+ dns_nta_t *n = (dns_nta_t *) node->data;
+ char nbuf[DNS_NAME_FORMATSIZE];
+ char tbuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
+ char obuf[DNS_NAME_FORMATSIZE +
+ ISC_FORMATHTTPTIMESTAMP_SIZE +
+ sizeof("expired: \n")];
+ dns_fixedname_t fn;
+ dns_name_t *name;
+ isc_time_t t;
+
+ name = dns_fixedname_initname(&fn);
+ dns_rbt_fullnamefromnode(node, name);
+ dns_name_format(name, nbuf, sizeof(nbuf));
+ isc_time_set(&t, n->expiry, 0);
+ isc_time_formattimestamp(&t, tbuf, sizeof(tbuf));
+
+ snprintf(obuf, sizeof(obuf), "%s%s: %s %s",
+ first ? "" : "\n", nbuf,
+ n->expiry <= now ? "expired" : "expiry",
+ tbuf);
+ first = false;
+ result = putstr(buf, obuf);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+ result = dns_rbtnodechain_next(&chain, NULL, NULL);
+ if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ break;
+ }
+ }
+
+ cleanup:
+ dns_rbtnodechain_invalidate(&chain);
+ RWUNLOCK(&ntatable->rwlock, isc_rwlocktype_read);
+ return (result);
+}
+
+#if 0
+isc_result_t
+dns_ntatable_dump(dns_ntatable_t *ntatable, FILE *fp) {
+ isc_result_t result;
+ dns_rbtnode_t *node;
+ dns_rbtnodechain_t chain;
+ isc_stdtime_t now;
+
+ REQUIRE(VALID_NTATABLE(ntatable));
+
+ isc_stdtime_get(&now);
+
+ RWLOCK(&ntatable->rwlock, isc_rwlocktype_read);
+ dns_rbtnodechain_init(&chain, ntatable->view->mctx);
+ result = dns_rbtnodechain_first(&chain, ntatable->table, NULL, NULL);
+ if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN)
+ goto cleanup;
+ for (;;) {
+ dns_rbtnodechain_current(&chain, NULL, NULL, &node);
+ if (node->data != NULL) {
+ dns_nta_t *n = (dns_nta_t *) node->data;
+ char nbuf[DNS_NAME_FORMATSIZE], tbuf[80];
+ dns_fixedname_t fn;
+ dns_name_t *name;
+ isc_time_t t;
+
+ name = dns_fixedname_initname(&fn);
+ dns_rbt_fullnamefromnode(node, name);
+ dns_name_format(name, nbuf, sizeof(nbuf));
+ isc_time_set(&t, n->expiry, 0);
+ isc_time_formattimestamp(&t, tbuf, sizeof(tbuf));
+ fprintf(fp, "%s: %s %s\n", nbuf,
+ n->expiry <= now ? "expired" : "expiry",
+ tbuf);
+ }
+ result = dns_rbtnodechain_next(&chain, NULL, NULL);
+ if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ break;
+ }
+ }
+
+ cleanup:
+ dns_rbtnodechain_invalidate(&chain);
+ RWUNLOCK(&ntatable->rwlock, isc_rwlocktype_read);
+ return (result);
+}
+#endif
+
+isc_result_t
+dns_ntatable_dump(dns_ntatable_t *ntatable, FILE *fp) {
+ isc_result_t result;
+ isc_buffer_t *text = NULL;
+ int len = 4096;
+
+ result = isc_buffer_allocate(ntatable->view->mctx, &text, len);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = dns_ntatable_totext(ntatable, &text);
+
+ if (isc_buffer_usedlength(text) != 0) {
+ (void) putstr(&text, "\n");
+ } else if (result == ISC_R_SUCCESS) {
+ (void) putstr(&text, "none");
+ } else {
+ (void) putstr(&text, "could not dump NTA table: ");
+ (void) putstr(&text, isc_result_totext(result));
+ }
+
+ fprintf(fp, "%.*s", (int) isc_buffer_usedlength(text),
+ (char *) isc_buffer_base(text));
+ isc_buffer_free(&text);
+ return (result);
+}
+
+isc_result_t
+dns_ntatable_save(dns_ntatable_t *ntatable, FILE *fp) {
+ isc_result_t result;
+ dns_rbtnode_t *node;
+ dns_rbtnodechain_t chain;
+ isc_stdtime_t now;
+ bool written = false;
+
+ REQUIRE(VALID_NTATABLE(ntatable));
+
+ isc_stdtime_get(&now);
+
+ RWLOCK(&ntatable->rwlock, isc_rwlocktype_read);
+ dns_rbtnodechain_init(&chain, ntatable->view->mctx);
+ result = dns_rbtnodechain_first(&chain, ntatable->table, NULL, NULL);
+ if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN)
+ goto cleanup;
+
+ for (;;) {
+ dns_rbtnodechain_current(&chain, NULL, NULL, &node);
+ if (node->data != NULL) {
+ dns_nta_t *n = (dns_nta_t *) node->data;
+ if (n->expiry > now) {
+ isc_buffer_t b;
+ char nbuf[DNS_NAME_FORMATSIZE + 1], tbuf[80];
+ dns_fixedname_t fn;
+ dns_name_t *name;
+
+ name = dns_fixedname_initname(&fn);
+ dns_rbt_fullnamefromnode(node, name);
+
+ isc_buffer_init(&b, nbuf, sizeof(nbuf));
+ result = dns_name_totext(name, false, &b);
+ if (result != ISC_R_SUCCESS)
+ goto skip;
+
+ /* Zero terminate. */
+ isc_buffer_putuint8(&b, 0);
+
+ isc_buffer_init(&b, tbuf, sizeof(tbuf));
+ dns_time32_totext(n->expiry, &b);
+
+ /* Zero terminate. */
+ isc_buffer_putuint8(&b, 0);
+
+ fprintf(fp, "%s %s %s\n", nbuf,
+ n->forced ? "forced" : "regular",
+ tbuf);
+ written = true;
+ }
+ }
+ skip:
+ result = dns_rbtnodechain_next(&chain, NULL, NULL);
+ if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ break;
+ }
+ }
+
+ cleanup:
+ dns_rbtnodechain_invalidate(&chain);
+ RWUNLOCK(&ntatable->rwlock, isc_rwlocktype_read);
+
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ else
+ return (written ? ISC_R_SUCCESS : ISC_R_NOTFOUND);
+}
diff --git a/lib/dns/openssl_link.c b/lib/dns/openssl_link.c
new file mode 100644
index 0000000..a30a2ab
--- /dev/null
+++ b/lib/dns/openssl_link.c
@@ -0,0 +1,464 @@
+/*
+ * Portions Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Portions Copyright (C) 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.
+ */
+
+#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 <dns/log.h>
+
+#include <dst/result.h>
+
+#include "dst_internal.h"
+#include "dst_openssl.h"
+
+#if !defined(OPENSSL_NO_ENGINE)
+#include <openssl/engine.h>
+#endif
+
+static RAND_METHOD *rm = NULL;
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+static isc_mutex_t *locks = NULL;
+static int nlocks;
+#endif
+
+#if !defined(OPENSSL_NO_ENGINE)
+static ENGINE *e = NULL;
+#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, false);
+ return (result == ISC_R_SUCCESS ? 1 : -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, true);
+ return (result == ISC_R_SUCCESS ? 1 : -1);
+}
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+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);
+}
+#else
+static int
+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);
+ return (1);
+}
+#endif
+
+#if OPENSSL_VERSION_NUMBER >= 0x10000000L && OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+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]);
+}
+#endif
+
+#if OPENSSL_VERSION_NUMBER < 0x10000000L || defined(LIBRESSL_VERSION_NUMBER)
+static unsigned long
+id_callback(void) {
+ return ((unsigned long)isc_thread_self());
+}
+#endif
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+
+#define FLARG
+#define FILELINE
+#if ISC_MEM_TRACKLINES
+#define FLARG_PASS , __FILE__, __LINE__
+#else
+#define FLARG_PASS
+#endif
+
+#else
+
+#define FLARG , const char *file, int line
+#define FILELINE , __FILE__, __LINE__
+#if ISC_MEM_TRACKLINES
+#define FLARG_PASS , file, line
+#else
+#define FLARG_PASS
+#endif
+
+#endif
+
+static void *
+mem_alloc(size_t size FLARG) {
+#ifdef OPENSSL_LEAKS
+ void *ptr;
+
+ INSIST(dst__memory_pool != NULL);
+ ptr = isc__mem_allocate(dst__memory_pool, size FLARG_PASS);
+ return (ptr);
+#else
+ INSIST(dst__memory_pool != NULL);
+ return (isc__mem_allocate(dst__memory_pool, size FLARG_PASS));
+#endif
+}
+
+static void
+mem_free(void *ptr FLARG) {
+ INSIST(dst__memory_pool != NULL);
+ if (ptr != NULL)
+ isc__mem_free(dst__memory_pool, ptr FLARG_PASS);
+}
+
+static void *
+mem_realloc(void *ptr, size_t size FLARG) {
+#ifdef OPENSSL_LEAKS
+ void *rptr;
+
+ INSIST(dst__memory_pool != NULL);
+ rptr = isc__mem_reallocate(dst__memory_pool, ptr, size FLARG_PASS);
+ return (rptr);
+#else
+ INSIST(dst__memory_pool != NULL);
+ return (isc__mem_reallocate(dst__memory_pool, ptr, size FLARG_PASS));
+#endif
+}
+
+#if OPENSSL_VERSION_NUMBER >= 0x10000000L && OPENSSL_VERSION_NUMBER < 0x10100000L
+static void
+_set_thread_id(CRYPTO_THREADID *id)
+{
+ CRYPTO_THREADID_set_numeric(id, (unsigned long)isc_thread_self());
+}
+#endif
+
+isc_result_t
+dst__openssl_init(const char *engine) {
+ isc_result_t result;
+#if !defined(OPENSSL_NO_ENGINE)
+ ENGINE *re;
+#else
+ UNUSED(engine);
+#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);
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+ nlocks = CRYPTO_num_locks();
+ locks = mem_alloc(sizeof(isc_mutex_t) * nlocks FILELINE);
+ 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);
+# if OPENSSL_VERSION_NUMBER >= 0x10000000L && OPENSSL_VERSION_NUMBER < 0x10100000L
+ CRYPTO_THREADID_set_callback(_set_thread_id);
+# else
+ CRYPTO_set_id_callback(id_callback);
+# endif
+
+ ERR_load_crypto_strings();
+#endif
+
+ rm = mem_alloc(sizeof(RAND_METHOD) FILELINE);
+ 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;
+
+#if !defined(OPENSSL_NO_ENGINE)
+#if !defined(CONF_MFLAGS_DEFAULT_SECTION)
+ OPENSSL_config(NULL);
+#else
+ /*
+ * OPENSSL_config() can only be called a single time as of
+ * 1.0.2e so do the steps individually.
+ */
+ OPENSSL_load_builtin_modules();
+ ENGINE_load_builtin_engines();
+ ERR_clear_error();
+ CONF_modules_load_file(NULL, NULL,
+ CONF_MFLAGS_DEFAULT_SECTION |
+ CONF_MFLAGS_IGNORE_MISSING_FILE);
+#endif
+
+ if (engine != NULL && *engine == '\0')
+ engine = NULL;
+
+ if (engine != NULL) {
+ e = ENGINE_by_id(engine);
+ if (e == NULL) {
+ result = DST_R_NOENGINE;
+ goto cleanup_rm;
+ }
+ /* This will init the engine. */
+ if (!ENGINE_set_default(e, ENGINE_METHOD_ALL)) {
+ result = DST_R_NOENGINE;
+ goto cleanup_rm;
+ }
+ }
+
+ 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 /* !defined(OPENSSL_NO_ENGINE) */
+ return (ISC_R_SUCCESS);
+
+#if !defined(OPENSSL_NO_ENGINE)
+ cleanup_rm:
+ if (e != NULL)
+ ENGINE_free(e);
+ e = NULL;
+ mem_free(rm FILELINE);
+ rm = NULL;
+#endif
+ cleanup_mutexinit:
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+ CRYPTO_set_locking_callback(NULL);
+ DESTROYMUTEXBLOCK(locks, nlocks);
+ cleanup_mutexalloc:
+ mem_free(locks FILELINE);
+ locks = NULL;
+#endif
+ return (result);
+}
+
+void
+dst__openssl_destroy(void) {
+#if !defined(LIBRESSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x10100000L)
+ OPENSSL_cleanup();
+ if (rm != NULL) {
+ mem_free(rm FILELINE);
+ rm = NULL;
+ }
+#else
+ /*
+ * Sequence taken from apps_shutdown() in <apps/apps.h>.
+ */
+ if (rm != NULL) {
+#if OPENSSL_VERSION_NUMBER >= 0x00907000L
+ RAND_cleanup();
+#endif
+ mem_free(rm FILELINE);
+ rm = NULL;
+ }
+#if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
+ CONF_modules_free();
+#endif
+ OBJ_cleanup();
+ EVP_cleanup();
+#if !defined(OPENSSL_NO_ENGINE)
+ if (e != NULL)
+ ENGINE_free(e);
+ e = NULL;
+#if !defined(OPENSSL_NO_ENGINE) && OPENSSL_VERSION_NUMBER >= 0x00907000L
+ ENGINE_cleanup();
+#endif
+#endif
+#if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
+ CRYPTO_cleanup_all_ex_data();
+#endif
+ ERR_clear_error();
+#if OPENSSL_VERSION_NUMBER >= 0x10000000L && OPENSSL_VERSION_NUMBER < 0x10100000L
+ ERR_remove_thread_state(NULL);
+#elif OPENSSL_VERSION_NUMBER < 0x10000000L || defined(LIBRESSL_VERSION_NUMBER)
+ ERR_remove_state(0);
+#endif
+ ERR_free_strings();
+
+#ifdef DNS_CRYPTO_LEAKS
+ CRYPTO_mem_leaks_fp(stderr);
+#endif
+
+ if (locks != NULL) {
+ CRYPTO_set_locking_callback(NULL);
+ DESTROYMUTEXBLOCK(locks, nlocks);
+ mem_free(locks FILELINE);
+ locks = NULL;
+ }
+#endif
+}
+
+static isc_result_t
+toresult(isc_result_t fallback) {
+ isc_result_t result = fallback;
+ unsigned long err = ERR_peek_error();
+#if defined(HAVE_OPENSSL_ECDSA) && \
+ defined(ECDSA_R_RANDOM_NUMBER_GENERATION_FAILED)
+ int lib = ERR_GET_LIB(err);
+#endif
+ int reason = ERR_GET_REASON(err);
+
+ switch (reason) {
+ /*
+ * ERR_* errors are globally unique; others
+ * are unique per sublibrary
+ */
+ case ERR_R_MALLOC_FAILURE:
+ result = ISC_R_NOMEMORY;
+ break;
+ default:
+#if defined(HAVE_OPENSSL_ECDSA) && \
+ defined(ECDSA_R_RANDOM_NUMBER_GENERATION_FAILED)
+ if (lib == ERR_R_ECDSA_LIB &&
+ reason == ECDSA_R_RANDOM_NUMBER_GENERATION_FAILED) {
+ result = ISC_R_NOENTROPY;
+ break;
+ }
+#endif
+ break;
+ }
+
+ return (result);
+}
+
+isc_result_t
+dst__openssl_toresult(isc_result_t fallback) {
+ isc_result_t result;
+
+ result = toresult(fallback);
+
+ ERR_clear_error();
+ return (result);
+}
+
+isc_result_t
+dst__openssl_toresult2(const char *funcname, isc_result_t fallback) {
+ return (dst__openssl_toresult3(DNS_LOGCATEGORY_GENERAL,
+ funcname, fallback));
+}
+
+isc_result_t
+dst__openssl_toresult3(isc_logcategory_t *category,
+ const char *funcname, isc_result_t fallback) {
+ isc_result_t result;
+ unsigned long err;
+ const char *file, *data;
+ int line, flags;
+ char buf[256];
+
+ result = toresult(fallback);
+
+ isc_log_write(dns_lctx, category,
+ DNS_LOGMODULE_CRYPTO, ISC_LOG_WARNING,
+ "%s failed (%s)", funcname,
+ isc_result_totext(result));
+
+ if (result == ISC_R_NOMEMORY)
+ goto done;
+
+ for (;;) {
+ err = ERR_get_error_line_data(&file, &line, &data, &flags);
+ if (err == 0U)
+ goto done;
+ ERR_error_string_n(err, buf, sizeof(buf));
+ isc_log_write(dns_lctx, category,
+ DNS_LOGMODULE_CRYPTO, ISC_LOG_INFO,
+ "%s:%s:%d:%s", buf, file, line,
+ (flags & ERR_TXT_STRING) ? data : "");
+ }
+
+ done:
+ ERR_clear_error();
+ return (result);
+}
+
+#if !defined(OPENSSL_NO_ENGINE)
+ENGINE *
+dst__openssl_getengine(const char *engine) {
+
+ if (engine == NULL)
+ return (NULL);
+ if (e == NULL)
+ return (NULL);
+ if (strcmp(engine, ENGINE_get_id(e)) == 0)
+ return (e);
+ return (NULL);
+}
+#endif
+
+#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..e8a3f7e
--- /dev/null
+++ b/lib/dns/openssldh_link.c
@@ -0,0 +1,793 @@
+/*
+ * Portions Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Portions Copyright (C) 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.
+ */
+
+#ifdef OPENSSL
+
+#include <config.h>
+
+#include <pk11/site.h>
+
+#ifndef PK11_DH_DISABLE
+
+#include <ctype.h>
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/mem.h>
+#include <isc/safe.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dst/result.h>
+
+#include <openssl/opensslv.h>
+
+#include "dst_internal.h"
+#include "dst_openssl.h"
+#include "dst_parse.h"
+
+#define PRIME2 "02"
+
+#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 = NULL, *bn768 = NULL, *bn1024 = NULL, *bn1536 = NULL;
+
+#if !defined(HAVE_DH_GET0_KEY)
+/*
+ * DH_get0_key, DH_set0_key, DH_get0_pqg and DH_set0_pqg
+ * are from OpenSSL 1.1.0.
+ */
+static void
+DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key) {
+ if (pub_key != NULL) {
+ *pub_key = dh->pub_key;
+ }
+ if (priv_key != NULL) {
+ *priv_key = dh->priv_key;
+ }
+}
+
+static int
+DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key) {
+ if (pub_key != NULL) {
+ BN_free(dh->pub_key);
+ dh->pub_key = pub_key;
+ }
+
+ if (priv_key != NULL) {
+ BN_free(dh->priv_key);
+ dh->priv_key = priv_key;
+ }
+
+ return (1);
+}
+
+static void
+DH_get0_pqg(const DH *dh,
+ const BIGNUM **p, const BIGNUM **q, const BIGNUM **g)
+{
+ if (p != NULL) {
+ *p = dh->p;
+ }
+ if (q != NULL) {
+ *q = dh->q;
+ }
+ if (g != NULL) {
+ *g = dh->g;
+ }
+}
+
+static int
+DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g)
+{
+ /* If the fields p and g in d are NULL, the corresponding input
+ * parameters MUST be non-NULL. q may remain NULL.
+ */
+ if ((dh->p == NULL && p == NULL)
+ || (dh->g == NULL && g == NULL))
+ {
+ return 0;
+ }
+
+ if (p != NULL) {
+ BN_free(dh->p);
+ dh->p = p;
+ }
+ if (q != NULL) {
+ BN_free(dh->q);
+ dh->q = q;
+ }
+ if (g != NULL) {
+ BN_free(dh->g);
+ dh->g = g;
+ }
+
+ if (q != NULL) {
+ dh->length = BN_num_bits(q);
+ }
+
+ return (1);
+}
+
+#define DH_clear_flags(d, f) (d)->flags &= ~(f)
+
+#endif
+
+static isc_result_t
+openssldh_computesecret(const dst_key_t *pub, const dst_key_t *priv,
+ isc_buffer_t *secret)
+{
+ DH *dhpub, *dhpriv;
+ const BIGNUM *pub_key = NULL;
+ 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);
+
+ DH_get0_key(dhpub, &pub_key, NULL);
+ ret = DH_compute_key(r.base, pub_key, dhpriv);
+ if (ret <= 0)
+ return (dst__openssl_toresult2("DH_compute_key",
+ DST_R_COMPUTESECRETFAILURE));
+ isc_buffer_add(secret, len);
+ return (ISC_R_SUCCESS);
+}
+
+static bool
+openssldh_compare(const dst_key_t *key1, const dst_key_t *key2) {
+ DH *dh1, *dh2;
+ const BIGNUM *pub_key1 = NULL, *pub_key2 = NULL;
+ const BIGNUM *priv_key1 = NULL, *priv_key2 = NULL;
+ const BIGNUM *p1 = NULL, *g1 = NULL, *p2 = NULL, *g2 = NULL;
+
+ dh1 = key1->keydata.dh;
+ dh2 = key2->keydata.dh;
+
+ if (dh1 == NULL && dh2 == NULL)
+ return (true);
+ else if (dh1 == NULL || dh2 == NULL)
+ return (false);
+
+ DH_get0_key(dh1, &pub_key1, &priv_key1);
+ DH_get0_key(dh2, &pub_key2, &priv_key2);
+ DH_get0_pqg(dh1, &p1, NULL, &g1);
+ DH_get0_pqg(dh2, &p2, NULL, &g2);
+
+ if (BN_cmp(p1, p2) != 0 || BN_cmp(g1, g2) != 0 ||
+ BN_cmp(pub_key1, pub_key2) != 0)
+ return (false);
+
+ if (priv_key1 != NULL || priv_key2 != NULL) {
+ if (priv_key1 == NULL || priv_key2 == NULL)
+ return (false);
+ if (BN_cmp(priv_key1, priv_key2) != 0)
+ return (false);
+ }
+ return (true);
+}
+
+static bool
+openssldh_paramcompare(const dst_key_t *key1, const dst_key_t *key2) {
+ DH *dh1, *dh2;
+ const BIGNUM *p1 = NULL, *g1 = NULL, *p2 = NULL, *g2 = NULL;
+
+ dh1 = key1->keydata.dh;
+ dh2 = key2->keydata.dh;
+
+ if (dh1 == NULL && dh2 == NULL)
+ return (true);
+ else if (dh1 == NULL || dh2 == NULL)
+ return (false);
+
+ DH_get0_pqg(dh1, &p1, NULL, &g1);
+ DH_get0_pqg(dh2, &p2, NULL, &g2);
+
+ if (BN_cmp(p1, p2) != 0 || BN_cmp(g1, g2) != 0)
+ return (false);
+ return (true);
+}
+
+#if OPENSSL_VERSION_NUMBER > 0x00908000L
+static int
+progress_cb(int p, int n, BN_GENCB *cb) {
+ union {
+ void *dptr;
+ void (*fptr)(int);
+ } u;
+
+ UNUSED(n);
+
+ u.dptr = BN_GENCB_get_arg(cb);
+ if (u.fptr != NULL)
+ u.fptr(p);
+ return (1);
+}
+#endif
+
+static isc_result_t
+openssldh_generate(dst_key_t *key, int generator, void (*callback)(int)) {
+ DH *dh = NULL;
+#if OPENSSL_VERSION_NUMBER > 0x00908000L
+ BN_GENCB *cb;
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+ BN_GENCB _cb;
+#endif
+ union {
+ void *dptr;
+ void (*fptr)(int);
+ } u;
+#else
+
+ UNUSED(callback);
+#endif
+
+ if (generator == 0) {
+ if (key->key_size == 768 ||
+ key->key_size == 1024 ||
+ key->key_size == 1536)
+ {
+ BIGNUM *p, *g;
+ dh = DH_new();
+ if (key->key_size == 768)
+ p = BN_dup(bn768);
+ else if (key->key_size == 1024)
+ p = BN_dup(bn1024);
+ else
+ p = BN_dup(bn1536);
+ g = BN_dup(bn2);
+ if (dh == NULL || p == NULL || g == NULL) {
+ if (dh != NULL)
+ DH_free(dh);
+ if (p != NULL)
+ BN_free(p);
+ if (g != NULL)
+ BN_free(g);
+ return (dst__openssl_toresult(ISC_R_NOMEMORY));
+ }
+ DH_set0_pqg(dh, p, NULL, g);
+ } else
+ generator = 2;
+ }
+
+ if (generator != 0) {
+#if OPENSSL_VERSION_NUMBER > 0x00908000L
+ dh = DH_new();
+ if (dh == NULL)
+ return (dst__openssl_toresult(ISC_R_NOMEMORY));
+ cb = BN_GENCB_new();
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
+ if (cb == NULL) {
+ DH_free(dh);
+ return (dst__openssl_toresult(ISC_R_NOMEMORY));
+ }
+#endif
+ if (callback == NULL) {
+ BN_GENCB_set_old(cb, NULL, NULL);
+ } else {
+ u.fptr = callback;
+ BN_GENCB_set(cb, &progress_cb, u.dptr);
+ }
+
+ if (!DH_generate_parameters_ex(dh, key->key_size, generator,
+ cb)) {
+ DH_free(dh);
+ BN_GENCB_free(cb);
+ return (dst__openssl_toresult2(
+ "DH_generate_parameters_ex",
+ DST_R_OPENSSLFAILURE));
+ }
+ BN_GENCB_free(cb);
+ cb = NULL;
+#else
+ dh = DH_generate_parameters(key->key_size, generator,
+ NULL, NULL);
+ if (dh == NULL)
+ return (dst__openssl_toresult2(
+ "DH_generate_parameters",
+ DST_R_OPENSSLFAILURE));
+#endif
+ }
+
+ if (DH_generate_key(dh) == 0) {
+ DH_free(dh);
+ return (dst__openssl_toresult2("DH_generate_key",
+ DST_R_OPENSSLFAILURE));
+ }
+ DH_clear_flags(dh, DH_FLAG_CACHE_MONT_P);
+ key->keydata.dh = dh;
+
+ return (ISC_R_SUCCESS);
+}
+
+static bool
+openssldh_isprivate(const dst_key_t *key) {
+ DH *dh = key->keydata.dh;
+ const BIGNUM *priv_key = NULL;
+
+ DH_get0_key(dh, NULL, &priv_key);
+ return (dh != NULL && priv_key != NULL);
+}
+
+static void
+openssldh_destroy(dst_key_t *key) {
+ DH *dh = key->keydata.dh;
+
+ if (dh == NULL)
+ return;
+
+ DH_free(dh);
+ key->keydata.dh = NULL;
+}
+
+static void
+uint16_toregion(uint16_t val, isc_region_t *region) {
+ *region->base = (val & 0xff00) >> 8;
+ isc_region_consume(region, 1);
+ *region->base = (val & 0x00ff);
+ isc_region_consume(region, 1);
+}
+
+static uint16_t
+uint16_fromregion(isc_region_t *region) {
+ uint16_t val;
+ unsigned char *cp = region->base;
+
+ val = ((unsigned int)(cp[0])) << 8;
+ val |= ((unsigned int)(cp[1]));
+
+ isc_region_consume(region, 2);
+
+ return (val);
+}
+
+static isc_result_t
+openssldh_todns(const dst_key_t *key, isc_buffer_t *data) {
+ DH *dh;
+ const BIGNUM *pub_key = NULL, *p = NULL, *g = NULL;
+ isc_region_t r;
+ uint16_t dnslen, plen, glen, publen;
+
+ REQUIRE(key->keydata.dh != NULL);
+
+ dh = key->keydata.dh;
+
+ isc_buffer_availableregion(data, &r);
+
+ DH_get0_pqg(dh, &p, NULL, &g);
+ if (BN_cmp(g, bn2) == 0 &&
+ (BN_cmp(p, bn768) == 0 ||
+ BN_cmp(p, bn1024) == 0 ||
+ BN_cmp(p, bn1536) == 0)) {
+ plen = 1;
+ glen = 0;
+ }
+ else {
+ plen = BN_num_bytes(p);
+ glen = BN_num_bytes(g);
+ }
+ DH_get0_key(dh, &pub_key, NULL);
+ publen = BN_num_bytes(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 (BN_cmp(p, bn768) == 0)
+ *r.base = 1;
+ else if (BN_cmp(p, bn1024) == 0)
+ *r.base = 2;
+ else
+ *r.base = 3;
+ } else
+ BN_bn2bin(p, r.base);
+ isc_region_consume(&r, plen);
+
+ uint16_toregion(glen, &r);
+ if (glen > 0)
+ BN_bn2bin(g, r.base);
+ isc_region_consume(&r, glen);
+
+ uint16_toregion(publen, &r);
+ BN_bn2bin(pub_key, r.base);
+ isc_region_consume(&r, 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;
+ BIGNUM *pub_key = NULL, *p = NULL, *g = NULL;
+ isc_region_t r;
+ 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_clear_flags(dh, 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;
+ isc_region_consume(&r, 1);
+ } else {
+ special = uint16_fromregion(&r);
+ }
+ switch (special) {
+ case 1:
+ p = BN_dup(bn768);
+ break;
+ case 2:
+ p = BN_dup(bn1024);
+ break;
+ case 3:
+ p = BN_dup(bn1536);
+ break;
+ default:
+ DH_free(dh);
+ return (DST_R_INVALIDPUBLICKEY);
+ }
+ } else {
+ p = BN_bin2bn(r.base, plen, NULL);
+ isc_region_consume(&r, 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)
+ g = BN_dup(bn2);
+ else {
+ g = BN_bin2bn(r.base, glen, NULL);
+ if (g != NULL && BN_cmp(g, bn2) != 0) {
+ DH_free(dh);
+ BN_free(g);
+ return (DST_R_INVALIDPUBLICKEY);
+ }
+ }
+ } else {
+ if (glen == 0) {
+ DH_free(dh);
+ return (DST_R_INVALIDPUBLICKEY);
+ }
+ g = BN_bin2bn(r.base, glen, NULL);
+ }
+ isc_region_consume(&r, glen);
+
+ if (p == NULL || g == NULL) {
+ DH_free(dh);
+ if (p != NULL)
+ BN_free(p);
+ if (g != NULL)
+ BN_free(g);
+ return (dst__openssl_toresult(ISC_R_NOMEMORY));
+ }
+ DH_set0_pqg(dh, p, NULL, g);
+
+ 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);
+ }
+ pub_key = BN_bin2bn(r.base, publen, NULL);
+ if (pub_key == NULL) {
+ DH_free(dh);
+ return (dst__openssl_toresult(ISC_R_NOMEMORY));
+ }
+#if (LIBRESSL_VERSION_NUMBER >= 0x2070000fL) && (LIBRESSL_VERSION_NUMBER <= 0x2070200fL)
+ /*
+ * LibreSSL << 2.7.3 DH_get0_key requires priv_key to be set when
+ * DH structure is empty, hence we cannot use DH_get0_key().
+ */
+ dh->pub_key = pub_key;
+#else /* LIBRESSL_VERSION_NUMBER */
+ DH_set0_key(dh, pub_key, NULL);
+#endif /* LIBRESSL_VERSION_NUMBER */
+ isc_region_consume(&r, publen);
+
+ key->key_size = BN_num_bits(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;
+ const BIGNUM *pub_key = NULL, *priv_key = NULL, *p = NULL, *g = NULL;
+ dst_private_t priv;
+ unsigned char *bufs[4];
+ isc_result_t result;
+
+ if (key->keydata.dh == NULL)
+ return (DST_R_NULLKEY);
+
+ if (key->external)
+ return (DST_R_EXTERNALKEY);
+
+ dh = key->keydata.dh;
+ DH_get0_key(dh, &pub_key, &priv_key);
+ DH_get0_pqg(dh, &p, NULL, &g);
+
+ memset(bufs, 0, sizeof(bufs));
+ for (i = 0; i < 4; i++) {
+ bufs[i] = isc_mem_get(key->mctx, BN_num_bytes(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(p);
+ BN_bn2bin(p, bufs[i]);
+ priv.elements[i].data = bufs[i];
+ i++;
+
+ priv.elements[i].tag = TAG_DH_GENERATOR;
+ priv.elements[i].length = BN_num_bytes(g);
+ BN_bn2bin(g, bufs[i]);
+ priv.elements[i].data = bufs[i];
+ i++;
+
+ priv.elements[i].tag = TAG_DH_PRIVATE;
+ priv.elements[i].length = BN_num_bytes(priv_key);
+ BN_bn2bin(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(pub_key);
+ BN_bn2bin(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(p));
+ }
+ return (result);
+}
+
+static isc_result_t
+openssldh_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
+ dst_private_t priv;
+ isc_result_t ret;
+ int i;
+ DH *dh = NULL;
+ BIGNUM *pub_key = NULL, *priv_key = NULL, *p = NULL, *g = NULL;
+ isc_mem_t *mctx;
+#define DST_RET(a) {ret = a; goto err;}
+
+ UNUSED(pub);
+ 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);
+
+ if (key->external)
+ DST_RET(DST_R_EXTERNALKEY);
+
+ dh = DH_new();
+ if (dh == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ DH_clear_flags(dh, 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:
+ p = bn;
+ break;
+ case TAG_DH_GENERATOR:
+ g = bn;
+ break;
+ case TAG_DH_PRIVATE:
+ priv_key = bn;
+ break;
+ case TAG_DH_PUBLIC:
+ pub_key = bn;
+ break;
+ }
+ }
+ dst__privstruct_free(&priv, mctx);
+ DH_set0_key(dh, pub_key, priv_key);
+ DH_set0_pqg(dh, p, NULL, g);
+
+ key->key_size = BN_num_bits(p);
+ return (ISC_R_SUCCESS);
+
+ err:
+ if (p != NULL)
+ BN_free(p);
+ if (g != NULL)
+ BN_free(g);
+ if (pub_key != NULL)
+ BN_free(pub_key);
+ if (priv_key != NULL)
+ BN_free(priv_key);
+ openssldh_destroy(key);
+ dst__privstruct_free(&priv, mctx);
+ isc_safe_memwipe(&priv, sizeof(priv));
+ return (ret);
+}
+
+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, /*%< createctx2 */
+ NULL, /*%< destroyctx */
+ NULL, /*%< adddata */
+ NULL, /*%< openssldh_sign */
+ NULL, /*%< openssldh_verify */
+ NULL, /*%< openssldh_verify2 */
+ openssldh_computesecret,
+ openssldh_compare,
+ openssldh_paramcompare,
+ openssldh_generate,
+ openssldh_isprivate,
+ openssldh_destroy,
+ openssldh_todns,
+ openssldh_fromdns,
+ openssldh_tofile,
+ openssldh_parse,
+ openssldh_cleanup,
+ NULL, /*%< fromlabel */
+ NULL, /*%< dump */
+ NULL, /*%< restore */
+};
+
+isc_result_t
+dst__openssldh_init(dst_func_t **funcp) {
+ REQUIRE(funcp != NULL);
+ if (*funcp == NULL) {
+ if (BN_hex2bn(&bn2, PRIME2) == 0 || bn2 == NULL) {
+ goto cleanup;
+ }
+ if (BN_hex2bn(&bn768, PRIME768) == 0 || bn768 == NULL) {
+ goto cleanup;
+ }
+ if (BN_hex2bn(&bn1024, PRIME1024) == 0 || bn1024 == NULL) {
+ goto cleanup;
+ }
+ if (BN_hex2bn(&bn1536, PRIME1536) == 0 || bn1536 == NULL) {
+ goto cleanup;
+ }
+ *funcp = &openssldh_functions;
+ }
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (bn2 != NULL) BN_free(bn2);
+ if (bn768 != NULL) BN_free(bn768);
+ if (bn1024 != NULL) BN_free(bn1024);
+ if (bn1536 != NULL) BN_free(bn1536);
+ return (ISC_R_NOMEMORY);
+}
+#endif /* !PK11_DH_DISABLE */
+
+#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..8abf4bb
--- /dev/null
+++ b/lib/dns/openssldsa_link.c
@@ -0,0 +1,815 @@
+/*
+ * Portions Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Portions Copyright (C) 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.
+ */
+
+#ifdef OPENSSL
+#ifndef USE_EVP
+#define USE_EVP 1
+#endif
+
+#include <config.h>
+
+#include <pk11/site.h>
+
+#ifndef PK11_DSA_DISABLE
+
+#include <stdbool.h>
+#include <string.h>
+
+#include <isc/entropy.h>
+#include <isc/mem.h>
+#include <isc/safe.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);
+
+#if !defined(HAVE_DSA_GET0_PQG)
+static void
+DSA_get0_pqg(const DSA *d, const BIGNUM **p, const BIGNUM **q,
+ const BIGNUM **g)
+{
+ if (p != NULL)
+ *p = d->p;
+ if (q != NULL)
+ *q = d->q;
+ if (g != NULL)
+ *g = d->g;
+}
+
+static int
+DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g) {
+ if (p == NULL || q == NULL || g == NULL)
+ return 0;
+ BN_free(d->p);
+ BN_free(d->q);
+ BN_free(d->g);
+ d->p = p;
+ d->q = q;
+ d->g = g;
+
+ return 1;
+}
+
+static void
+DSA_get0_key(const DSA *d, const BIGNUM **pub_key, const BIGNUM **priv_key) {
+ if (pub_key != NULL)
+ *pub_key = d->pub_key;
+ if (priv_key != NULL)
+ *priv_key = d->priv_key;
+}
+
+static int
+DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key) {
+ /* Note that it is valid for priv_key to be NULL */
+ if (pub_key == NULL)
+ return 0;
+
+ BN_free(d->pub_key);
+ BN_free(d->priv_key);
+ d->pub_key = pub_key;
+ d->priv_key = priv_key;
+
+ return 1;
+}
+
+static void
+DSA_SIG_get0(const DSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps) {
+ *pr = sig->r;
+ *ps = sig->s;
+}
+
+static int
+DSA_SIG_set0(DSA_SIG *sig, BIGNUM *r, BIGNUM *s) {
+ if (r == NULL || s == NULL)
+ return 0;
+
+ BN_clear_free(sig->r);
+ BN_clear_free(sig->s);
+ sig->r = r;
+ sig->s = s;
+
+ return 1;
+}
+
+
+#define DSA_clear_flags(d, x) (d)->flags &= ~(x)
+
+#endif
+
+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));
+ if (sha1ctx == NULL)
+ return (ISC_R_NOMEMORY);
+ 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(const 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 region;
+ DSA_SIG *dsasig;
+ const BIGNUM *r = 0, *s = NULL;
+ unsigned int klen;
+#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, &region);
+ if (region.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 (dst__openssl_toresult3(dctx->category,
+ "EVP_SignFinal",
+ 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 (dst__openssl_toresult3(dctx->category,
+ "d2i_DSA_SIG",
+ ISC_R_FAILURE));
+ }
+ free(sigbuf);
+
+#elif 0
+ /* Only use EVP for the Digest */
+ if (!EVP_DigestFinal_ex(evp_md_ctx, digest, &siglen)) {
+ return (dst__openssl_toresult3(dctx->category,
+ "EVP_DigestFinal_ex",
+ ISC_R_FAILURE));
+ }
+ dsasig = DSA_do_sign(digest, ISC_SHA1_DIGESTLENGTH, dsa);
+ if (dsasig == NULL)
+ return (dst__openssl_toresult3(dctx->category,
+ "DSA_do_sign",
+ DST_R_SIGNFAILURE));
+#else
+ isc_sha1_final(sha1ctx, digest);
+
+ dsasig = DSA_do_sign(digest, ISC_SHA1_DIGESTLENGTH, dsa);
+ if (dsasig == NULL)
+ return (dst__openssl_toresult3(dctx->category,
+ "DSA_do_sign",
+ DST_R_SIGNFAILURE));
+#endif
+
+ klen = (key->key_size - 512)/64;
+ if (klen > 255)
+ return (ISC_R_FAILURE);
+ *region.base = klen;
+ isc_region_consume(&region, 1);
+
+ DSA_SIG_get0(dsasig, &r, &s);
+ BN_bn2bin_fixed(r, region.base, ISC_SHA1_DIGESTLENGTH);
+ isc_region_consume(&region, ISC_SHA1_DIGESTLENGTH);
+ BN_bn2bin_fixed(s, region.base, ISC_SHA1_DIGESTLENGTH);
+ isc_region_consume(&region, 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;
+ BIGNUM *r = NULL, *s = NULL;
+ 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);
+ r = BN_bin2bn(cp, ISC_SHA1_DIGESTLENGTH, NULL);
+ cp += ISC_SHA1_DIGESTLENGTH;
+ s = BN_bin2bn(cp, ISC_SHA1_DIGESTLENGTH, NULL);
+ DSA_SIG_set0(dsasig, r, s);
+
+#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);
+ switch (status) {
+ case 1:
+ return (ISC_R_SUCCESS);
+ case 0:
+ return (dst__openssl_toresult(DST_R_VERIFYFAILURE));
+ default:
+ return (dst__openssl_toresult3(dctx->category,
+ "DSA_do_verify",
+ DST_R_VERIFYFAILURE));
+ }
+}
+
+static bool
+openssldsa_compare(const dst_key_t *key1, const dst_key_t *key2) {
+ DSA *dsa1, *dsa2;
+ const BIGNUM *pub_key1 = NULL, *priv_key1 = NULL;
+ const BIGNUM *pub_key2 = NULL, *priv_key2 = NULL;
+ const BIGNUM *p1 = NULL, *q1 = NULL, *g1 = NULL;
+ const BIGNUM *p2 = NULL, *q2 = NULL, *g2 = NULL;
+
+ dsa1 = key1->keydata.dsa;
+ dsa2 = key2->keydata.dsa;
+
+ if (dsa1 == NULL && dsa2 == NULL)
+ return (true);
+ else if (dsa1 == NULL || dsa2 == NULL)
+ return (false);
+
+ DSA_get0_key(dsa1, &pub_key1, &priv_key1);
+ DSA_get0_key(dsa2, &pub_key2, &priv_key2);
+ DSA_get0_pqg(dsa1, &p1, &q1, &g1);
+ DSA_get0_pqg(dsa2, &p2, &q2, &g2);
+
+ if (BN_cmp(p1, p2) != 0 || BN_cmp(q1, q2) != 0 ||
+ BN_cmp(g1, g2) != 0 || BN_cmp(pub_key1, pub_key2) != 0)
+ return (false);
+
+ if (priv_key1 != NULL || priv_key2 != NULL) {
+ if (priv_key1 == NULL || priv_key2 == NULL)
+ return (false);
+ if (BN_cmp(priv_key1, priv_key2))
+ return (false);
+ }
+ return (true);
+}
+
+#if OPENSSL_VERSION_NUMBER > 0x00908000L
+static int
+progress_cb(int p, int n, BN_GENCB *cb) {
+ union {
+ void *dptr;
+ void (*fptr)(int);
+ } u;
+
+ UNUSED(n);
+
+ u.dptr = BN_GENCB_get_arg(cb);
+ if (u.fptr != NULL)
+ u.fptr(p);
+ return (1);
+}
+#endif
+
+static isc_result_t
+openssldsa_generate(dst_key_t *key, int unused, void (*callback)(int)) {
+ DSA *dsa;
+ unsigned char rand_array[ISC_SHA1_DIGESTLENGTH];
+ isc_result_t result;
+#if OPENSSL_VERSION_NUMBER > 0x00908000L
+ BN_GENCB *cb;
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+ BN_GENCB _cb;
+#endif
+ union {
+ void *dptr;
+ void (*fptr)(int);
+ } u;
+
+#else
+
+ UNUSED(callback);
+#endif
+ UNUSED(unused);
+
+ result = dst__entropy_getdata(rand_array, sizeof(rand_array),
+ 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));
+ cb = BN_GENCB_new();
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
+ if (cb == NULL) {
+ DSA_free(dsa);
+ return (dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+ }
+#endif
+ if (callback == NULL) {
+ BN_GENCB_set_old(cb, NULL, NULL);
+ } else {
+ u.fptr = callback;
+ BN_GENCB_set(cb, &progress_cb, u.dptr);
+ }
+
+ if (!DSA_generate_parameters_ex(dsa, key->key_size, rand_array,
+ ISC_SHA1_DIGESTLENGTH, NULL, NULL,
+ cb))
+ {
+ DSA_free(dsa);
+ BN_GENCB_free(cb);
+ return (dst__openssl_toresult2("DSA_generate_parameters_ex",
+ DST_R_OPENSSLFAILURE));
+ }
+ BN_GENCB_free(cb);
+ cb = NULL;
+#else
+ dsa = DSA_generate_parameters(key->key_size, rand_array,
+ ISC_SHA1_DIGESTLENGTH, NULL, NULL,
+ NULL, NULL);
+ if (dsa == NULL)
+ return (dst__openssl_toresult2("DSA_generate_parameters",
+ DST_R_OPENSSLFAILURE));
+#endif
+
+ if (DSA_generate_key(dsa) == 0) {
+ DSA_free(dsa);
+ return (dst__openssl_toresult2("DSA_generate_key",
+ DST_R_OPENSSLFAILURE));
+ }
+
+ DSA_clear_flags(dsa, DSA_FLAG_CACHE_MONT_P);
+
+ key->keydata.dsa = dsa;
+
+ return (ISC_R_SUCCESS);
+}
+
+static bool
+openssldsa_isprivate(const dst_key_t *key) {
+ DSA *dsa = key->keydata.dsa;
+ const BIGNUM *priv_key = NULL;
+
+ DSA_get0_key(dsa, NULL, &priv_key);
+ return (dsa != NULL && 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;
+ const BIGNUM *pub_key, *p = NULL, *q = NULL, *g = NULL;
+ 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);
+
+ DSA_get0_key(dsa, &pub_key, NULL);
+ DSA_get0_pqg(dsa, &p, &q, &g);
+
+ t = (BN_num_bytes(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;
+ isc_region_consume(&r, 1);
+
+ BN_bn2bin_fixed(q, r.base, ISC_SHA1_DIGESTLENGTH);
+ isc_region_consume(&r, ISC_SHA1_DIGESTLENGTH);
+ BN_bn2bin_fixed(p, r.base, key->key_size/8);
+ isc_region_consume(&r, p_bytes);
+ BN_bn2bin_fixed(g, r.base, key->key_size/8);
+ isc_region_consume(&r, p_bytes);
+ BN_bn2bin_fixed(pub_key, r.base, key->key_size/8);
+ isc_region_consume(&r, 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;
+ BIGNUM *pub_key, *p, *q, *g;
+ 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_clear_flags(dsa, DSA_FLAG_CACHE_MONT_P);
+
+ t = (unsigned int) *r.base;
+ isc_region_consume(&r, 1);
+ if (t > 8) {
+ DSA_free(dsa);
+ return (DST_R_INVALIDPUBLICKEY);
+ }
+ p_bytes = 64 + 8 * t;
+
+ if (r.length < ISC_SHA1_DIGESTLENGTH + 3 * p_bytes) {
+ DSA_free(dsa);
+ return (DST_R_INVALIDPUBLICKEY);
+ }
+
+ q = BN_bin2bn(r.base, ISC_SHA1_DIGESTLENGTH, NULL);
+ isc_region_consume(&r, ISC_SHA1_DIGESTLENGTH);
+
+ p = BN_bin2bn(r.base, p_bytes, NULL);
+ isc_region_consume(&r, p_bytes);
+
+ g = BN_bin2bn(r.base, p_bytes, NULL);
+ isc_region_consume(&r, p_bytes);
+
+ pub_key = BN_bin2bn(r.base, p_bytes, NULL);
+ isc_region_consume(&r, p_bytes);
+
+ if (pub_key == NULL || p == NULL || q == NULL || g == NULL) {
+ DSA_free(dsa);
+ if (p != NULL) BN_free(p);
+ if (q != NULL) BN_free(q);
+ if (g != NULL) BN_free(g);
+ return (ISC_R_NOMEMORY);
+ }
+
+ DSA_set0_key(dsa, pub_key, NULL);
+ DSA_set0_pqg(dsa, p, q, g);
+
+ 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;
+ const BIGNUM *pub_key = NULL, *priv_key = NULL;
+ const BIGNUM *p = NULL, *q = NULL, *g = NULL;
+ dst_private_t priv;
+ unsigned char bufs[5][128];
+
+ if (key->keydata.dsa == NULL)
+ return (DST_R_NULLKEY);
+
+ if (key->external) {
+ priv.nelements = 0;
+ return (dst__privstruct_writefile(key, &priv, directory));
+ }
+
+ dsa = key->keydata.dsa;
+
+ DSA_get0_key(dsa, &pub_key, &priv_key);
+ DSA_get0_pqg(dsa, &p, &q, &g);
+
+ priv.elements[cnt].tag = TAG_DSA_PRIME;
+ priv.elements[cnt].length = BN_num_bytes(p);
+ BN_bn2bin(p, bufs[cnt]);
+ priv.elements[cnt].data = bufs[cnt];
+ cnt++;
+
+ priv.elements[cnt].tag = TAG_DSA_SUBPRIME;
+ priv.elements[cnt].length = BN_num_bytes(q);
+ BN_bn2bin(q, bufs[cnt]);
+ priv.elements[cnt].data = bufs[cnt];
+ cnt++;
+
+ priv.elements[cnt].tag = TAG_DSA_BASE;
+ priv.elements[cnt].length = BN_num_bytes(g);
+ BN_bn2bin(g, bufs[cnt]);
+ priv.elements[cnt].data = bufs[cnt];
+ cnt++;
+
+ priv.elements[cnt].tag = TAG_DSA_PRIVATE;
+ priv.elements[cnt].length = BN_num_bytes(priv_key);
+ BN_bn2bin(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(pub_key);
+ BN_bn2bin(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_key_t *pub) {
+ dst_private_t priv;
+ isc_result_t ret;
+ int i;
+ DSA *dsa = NULL;
+ BIGNUM *pub_key = NULL, *priv_key = NULL;
+ BIGNUM *p = NULL, *q = NULL, *g = 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);
+
+ if (key->external) {
+ if (priv.nelements != 0)
+ DST_RET(DST_R_INVALIDPRIVATEKEY);
+ if (pub == NULL)
+ DST_RET(DST_R_INVALIDPRIVATEKEY);
+ key->keydata.pkey = pub->keydata.pkey;
+ pub->keydata.pkey = NULL;
+ key->key_size = pub->key_size;
+ dst__privstruct_free(&priv, mctx);
+ isc_safe_memwipe(&priv, sizeof(priv));
+ return (ISC_R_SUCCESS);
+ }
+
+ dsa = DSA_new();
+ if (dsa == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ DSA_clear_flags(dsa, 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:
+ p = bn;
+ break;
+ case TAG_DSA_SUBPRIME:
+ q = bn;
+ break;
+ case TAG_DSA_BASE:
+ g = bn;
+ break;
+ case TAG_DSA_PRIVATE:
+ priv_key = bn;
+ break;
+ case TAG_DSA_PUBLIC:
+ pub_key = bn;
+ break;
+ }
+ }
+ dst__privstruct_free(&priv, mctx);
+ isc_safe_memwipe(&priv, sizeof(priv));
+ DSA_set0_key(dsa, pub_key, priv_key);
+ DSA_set0_pqg(dsa, p, q, g);
+ key->key_size = BN_num_bits(p);
+ return (ISC_R_SUCCESS);
+
+ err:
+ if (p != NULL)
+ BN_free(p);
+ if (q != NULL)
+ BN_free(q);
+ if (g != NULL)
+ BN_free(g);
+ openssldsa_destroy(key);
+ dst__privstruct_free(&priv, mctx);
+ isc_safe_memwipe(&priv, sizeof(priv));
+ return (ret);
+}
+
+static dst_func_t openssldsa_functions = {
+ openssldsa_createctx,
+ NULL, /*%< createctx2 */
+ openssldsa_destroyctx,
+ openssldsa_adddata,
+ openssldsa_sign,
+ openssldsa_verify,
+ NULL, /*%< verify2 */
+ NULL, /*%< computesecret */
+ openssldsa_compare,
+ NULL, /*%< paramcompare */
+ openssldsa_generate,
+ openssldsa_isprivate,
+ openssldsa_destroy,
+ openssldsa_todns,
+ openssldsa_fromdns,
+ openssldsa_tofile,
+ openssldsa_parse,
+ NULL, /*%< cleanup */
+ NULL, /*%< fromlabel */
+ NULL, /*%< dump */
+ NULL, /*%< restore */
+};
+
+isc_result_t
+dst__openssldsa_init(dst_func_t **funcp) {
+ REQUIRE(funcp != NULL);
+ if (*funcp == NULL)
+ *funcp = &openssldsa_functions;
+ return (ISC_R_SUCCESS);
+}
+#endif /* !PK11_DSA_DISABLE */
+
+#else /* OPENSSL */
+
+#include <isc/util.h>
+
+EMPTY_TRANSLATION_UNIT
+
+#endif /* OPENSSL */
+/*! \file */
diff --git a/lib/dns/opensslecdsa_link.c b/lib/dns/opensslecdsa_link.c
new file mode 100644
index 0000000..e9ea5ea
--- /dev/null
+++ b/lib/dns/opensslecdsa_link.c
@@ -0,0 +1,657 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <config.h>
+
+#if defined(OPENSSL) && defined(HAVE_OPENSSL_ECDSA)
+
+#if !defined(HAVE_EVP_SHA256) || !defined(HAVE_EVP_SHA384)
+#error "ECDSA without EVP for SHA2?"
+#endif
+
+#include <stdbool.h>
+
+
+#include <isc/entropy.h>
+#include <isc/mem.h>
+#include <isc/safe.h>
+#include <isc/sha2.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dns/keyvalues.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/ecdsa.h>
+#include <openssl/bn.h>
+
+#ifndef NID_X9_62_prime256v1
+#error "P-256 group is not known (NID_X9_62_prime256v1)"
+#endif
+#ifndef NID_secp384r1
+#error "P-384 group is not known (NID_secp384r1)"
+#endif
+
+#define DST_RET(a) {ret = a; goto err;}
+
+#if !defined(HAVE_ECDSA_SIG_GET0)
+/* From OpenSSL 1.1 */
+static void
+ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps) {
+ if (pr != NULL) {
+ *pr = sig->r;
+ }
+ if (ps != NULL) {
+ *ps = sig->s;
+ }
+}
+
+static int
+ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s) {
+ if (r == NULL || s == NULL) {
+ return 0;
+ }
+
+ BN_clear_free(sig->r);
+ BN_clear_free(sig->s);
+ sig->r = r;
+ sig->s = s;
+
+ return 1;
+}
+#endif
+
+static isc_result_t opensslecdsa_todns(const dst_key_t *key,
+ isc_buffer_t *data);
+
+static isc_result_t
+opensslecdsa_createctx(dst_key_t *key, dst_context_t *dctx) {
+ EVP_MD_CTX *evp_md_ctx;
+ const EVP_MD *type = NULL;
+
+ UNUSED(key);
+ REQUIRE(dctx->key->key_alg == DST_ALG_ECDSA256 ||
+ dctx->key->key_alg == DST_ALG_ECDSA384);
+
+ evp_md_ctx = EVP_MD_CTX_create();
+ if (evp_md_ctx == NULL)
+ return (ISC_R_NOMEMORY);
+ if (dctx->key->key_alg == DST_ALG_ECDSA256)
+ type = EVP_sha256();
+ else
+ type = EVP_sha384();
+
+ if (!EVP_DigestInit_ex(evp_md_ctx, type, NULL)) {
+ EVP_MD_CTX_destroy(evp_md_ctx);
+ return (dst__openssl_toresult3(dctx->category,
+ "EVP_DigestInit_ex",
+ ISC_R_FAILURE));
+ }
+
+ dctx->ctxdata.evp_md_ctx = evp_md_ctx;
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+opensslecdsa_destroyctx(dst_context_t *dctx) {
+ EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
+
+ REQUIRE(dctx->key->key_alg == DST_ALG_ECDSA256 ||
+ dctx->key->key_alg == DST_ALG_ECDSA384);
+
+ if (evp_md_ctx != NULL) {
+ EVP_MD_CTX_destroy(evp_md_ctx);
+ dctx->ctxdata.evp_md_ctx = NULL;
+ }
+}
+
+static isc_result_t
+opensslecdsa_adddata(dst_context_t *dctx, const isc_region_t *data) {
+ EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
+
+ REQUIRE(dctx->key->key_alg == DST_ALG_ECDSA256 ||
+ dctx->key->key_alg == DST_ALG_ECDSA384);
+
+ if (!EVP_DigestUpdate(evp_md_ctx, data->base, data->length))
+ return (dst__openssl_toresult3(dctx->category,
+ "EVP_DigestUpdate",
+ ISC_R_FAILURE));
+
+ return (ISC_R_SUCCESS);
+}
+
+static int
+BN_bn2bin_fixed(const 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
+opensslecdsa_sign(dst_context_t *dctx, isc_buffer_t *sig) {
+ isc_result_t ret;
+ dst_key_t *key = dctx->key;
+ isc_region_t region;
+ ECDSA_SIG *ecdsasig;
+ EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
+ EVP_PKEY *pkey = key->keydata.pkey;
+ EC_KEY *eckey = EVP_PKEY_get1_EC_KEY(pkey);
+ unsigned int dgstlen, siglen;
+ unsigned char digest[EVP_MAX_MD_SIZE];
+ const BIGNUM *r, *s;
+
+ REQUIRE(key->key_alg == DST_ALG_ECDSA256 ||
+ key->key_alg == DST_ALG_ECDSA384);
+
+ if (eckey == NULL)
+ return (ISC_R_FAILURE);
+
+ if (key->key_alg == DST_ALG_ECDSA256)
+ siglen = DNS_SIG_ECDSA256SIZE;
+ else
+ siglen = DNS_SIG_ECDSA384SIZE;
+
+ isc_buffer_availableregion(sig, &region);
+ if (region.length < siglen)
+ DST_RET(ISC_R_NOSPACE);
+
+ if (!EVP_DigestFinal(evp_md_ctx, digest, &dgstlen))
+ DST_RET(dst__openssl_toresult3(dctx->category,
+ "EVP_DigestFinal",
+ ISC_R_FAILURE));
+
+ ecdsasig = ECDSA_do_sign(digest, dgstlen, eckey);
+ if (ecdsasig == NULL)
+ DST_RET(dst__openssl_toresult3(dctx->category,
+ "ECDSA_do_sign",
+ DST_R_SIGNFAILURE));
+ ECDSA_SIG_get0(ecdsasig, &r, &s);
+ BN_bn2bin_fixed(r, region.base, siglen / 2);
+ isc_region_consume(&region, siglen / 2);
+ BN_bn2bin_fixed(s, region.base, siglen / 2);
+ isc_region_consume(&region, siglen / 2);
+ ECDSA_SIG_free(ecdsasig);
+ isc_buffer_add(sig, siglen);
+ ret = ISC_R_SUCCESS;
+
+ err:
+ if (eckey != NULL)
+ EC_KEY_free(eckey);
+ return (ret);
+}
+
+static isc_result_t
+opensslecdsa_verify(dst_context_t *dctx, const isc_region_t *sig) {
+ isc_result_t ret;
+ dst_key_t *key = dctx->key;
+ int status;
+ unsigned char *cp = sig->base;
+ ECDSA_SIG *ecdsasig = NULL;
+ EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
+ EVP_PKEY *pkey = key->keydata.pkey;
+ EC_KEY *eckey = EVP_PKEY_get1_EC_KEY(pkey);
+ unsigned int dgstlen, siglen;
+ unsigned char digest[EVP_MAX_MD_SIZE];
+ BIGNUM *r = NULL, *s = NULL ;
+
+ REQUIRE(key->key_alg == DST_ALG_ECDSA256 ||
+ key->key_alg == DST_ALG_ECDSA384);
+
+ if (eckey == NULL)
+ return (ISC_R_FAILURE);
+
+ if (key->key_alg == DST_ALG_ECDSA256)
+ siglen = DNS_SIG_ECDSA256SIZE;
+ else
+ siglen = DNS_SIG_ECDSA384SIZE;
+
+ if (sig->length != siglen)
+ return (DST_R_VERIFYFAILURE);
+
+ if (!EVP_DigestFinal_ex(evp_md_ctx, digest, &dgstlen))
+ DST_RET (dst__openssl_toresult3(dctx->category,
+ "EVP_DigestFinal_ex",
+ ISC_R_FAILURE));
+
+ ecdsasig = ECDSA_SIG_new();
+ if (ecdsasig == NULL)
+ DST_RET (ISC_R_NOMEMORY);
+ r = BN_bin2bn(cp, siglen / 2, NULL);
+ cp += siglen / 2;
+ s = BN_bin2bn(cp, siglen / 2, NULL);
+ ECDSA_SIG_set0(ecdsasig, r, s);
+ /* cp += siglen / 2; */
+
+ status = ECDSA_do_verify(digest, dgstlen, ecdsasig, eckey);
+ switch (status) {
+ case 1:
+ ret = ISC_R_SUCCESS;
+ break;
+ case 0:
+ ret = dst__openssl_toresult(DST_R_VERIFYFAILURE);
+ break;
+ default:
+ ret = dst__openssl_toresult3(dctx->category,
+ "ECDSA_do_verify",
+ DST_R_VERIFYFAILURE);
+ break;
+ }
+
+ err:
+ if (ecdsasig != NULL)
+ ECDSA_SIG_free(ecdsasig);
+ if (eckey != NULL)
+ EC_KEY_free(eckey);
+ return (ret);
+}
+
+static bool
+opensslecdsa_compare(const dst_key_t *key1, const dst_key_t *key2) {
+ bool ret;
+ int status;
+ EVP_PKEY *pkey1 = key1->keydata.pkey;
+ EVP_PKEY *pkey2 = key2->keydata.pkey;
+ EC_KEY *eckey1 = NULL;
+ EC_KEY *eckey2 = NULL;
+ const BIGNUM *priv1, *priv2;
+
+ if (pkey1 == NULL && pkey2 == NULL)
+ return (true);
+ else if (pkey1 == NULL || pkey2 == NULL)
+ return (false);
+
+ eckey1 = EVP_PKEY_get1_EC_KEY(pkey1);
+ eckey2 = EVP_PKEY_get1_EC_KEY(pkey2);
+ if (eckey1 == NULL && eckey2 == NULL) {
+ DST_RET (true);
+ } else if (eckey1 == NULL || eckey2 == NULL)
+ DST_RET (false);
+
+ status = EVP_PKEY_cmp(pkey1, pkey2);
+ if (status != 1)
+ DST_RET (false);
+
+ priv1 = EC_KEY_get0_private_key(eckey1);
+ priv2 = EC_KEY_get0_private_key(eckey2);
+ if (priv1 != NULL || priv2 != NULL) {
+ if (priv1 == NULL || priv2 == NULL)
+ DST_RET (false);
+ if (BN_cmp(priv1, priv2) != 0)
+ DST_RET (false);
+ }
+ ret = true;
+
+ err:
+ if (eckey1 != NULL)
+ EC_KEY_free(eckey1);
+ if (eckey2 != NULL)
+ EC_KEY_free(eckey2);
+ return (ret);
+}
+
+static isc_result_t
+opensslecdsa_generate(dst_key_t *key, int unused, void (*callback)(int)) {
+ isc_result_t ret;
+ EVP_PKEY *pkey;
+ EC_KEY *eckey = NULL;
+ int group_nid;
+
+ REQUIRE(key->key_alg == DST_ALG_ECDSA256 ||
+ key->key_alg == DST_ALG_ECDSA384);
+ UNUSED(unused);
+ UNUSED(callback);
+
+ if (key->key_alg == DST_ALG_ECDSA256) {
+ group_nid = NID_X9_62_prime256v1;
+ key->key_size = DNS_KEY_ECDSA256SIZE * 4;
+ } else {
+ group_nid = NID_secp384r1;
+ key->key_size = DNS_KEY_ECDSA384SIZE * 4;
+ }
+
+ eckey = EC_KEY_new_by_curve_name(group_nid);
+ if (eckey == NULL)
+ return (dst__openssl_toresult2("EC_KEY_new_by_curve_name",
+ DST_R_OPENSSLFAILURE));
+
+ if (EC_KEY_generate_key(eckey) != 1)
+ DST_RET (dst__openssl_toresult2("EC_KEY_generate_key",
+ DST_R_OPENSSLFAILURE));
+
+ pkey = EVP_PKEY_new();
+ if (pkey == NULL)
+ DST_RET (ISC_R_NOMEMORY);
+ if (!EVP_PKEY_set1_EC_KEY(pkey, eckey)) {
+ EVP_PKEY_free(pkey);
+ DST_RET (ISC_R_FAILURE);
+ }
+ key->keydata.pkey = pkey;
+ ret = ISC_R_SUCCESS;
+
+ err:
+ if (eckey != NULL)
+ EC_KEY_free(eckey);
+ return (ret);
+}
+
+static bool
+opensslecdsa_isprivate(const dst_key_t *key) {
+ bool ret;
+ EVP_PKEY *pkey = key->keydata.pkey;
+ EC_KEY *eckey = EVP_PKEY_get1_EC_KEY(pkey);
+
+ ret = (eckey != NULL && EC_KEY_get0_private_key(eckey) != NULL);
+ if (eckey != NULL)
+ EC_KEY_free(eckey);
+ return (ret);
+}
+
+static void
+opensslecdsa_destroy(dst_key_t *key) {
+ EVP_PKEY *pkey = key->keydata.pkey;
+
+ EVP_PKEY_free(pkey);
+ key->keydata.pkey = NULL;
+}
+
+static isc_result_t
+opensslecdsa_todns(const dst_key_t *key, isc_buffer_t *data) {
+ isc_result_t ret;
+ EVP_PKEY *pkey;
+ EC_KEY *eckey = NULL;
+ isc_region_t r;
+ int len;
+ unsigned char *cp;
+ unsigned char buf[DNS_KEY_ECDSA384SIZE + 1];
+
+ REQUIRE(key->keydata.pkey != NULL);
+
+ pkey = key->keydata.pkey;
+ eckey = EVP_PKEY_get1_EC_KEY(pkey);
+ if (eckey == NULL)
+ return (dst__openssl_toresult(ISC_R_FAILURE));
+ len = i2o_ECPublicKey(eckey, NULL);
+ /* skip form */
+ len--;
+
+ isc_buffer_availableregion(data, &r);
+ if (r.length < (unsigned int) len)
+ DST_RET (ISC_R_NOSPACE);
+ cp = buf;
+ if (!i2o_ECPublicKey(eckey, &cp))
+ DST_RET (dst__openssl_toresult(ISC_R_FAILURE));
+ memmove(r.base, buf + 1, len);
+ isc_buffer_add(data, len);
+ ret = ISC_R_SUCCESS;
+
+ err:
+ if (eckey != NULL)
+ EC_KEY_free(eckey);
+ return (ret);
+}
+
+static isc_result_t
+opensslecdsa_fromdns(dst_key_t *key, isc_buffer_t *data) {
+ isc_result_t ret;
+ EVP_PKEY *pkey;
+ EC_KEY *eckey = NULL;
+ isc_region_t r;
+ int group_nid;
+ unsigned int len;
+ const unsigned char *cp;
+ unsigned char buf[DNS_KEY_ECDSA384SIZE + 1];
+
+ REQUIRE(key->key_alg == DST_ALG_ECDSA256 ||
+ key->key_alg == DST_ALG_ECDSA384);
+
+ if (key->key_alg == DST_ALG_ECDSA256) {
+ len = DNS_KEY_ECDSA256SIZE;
+ group_nid = NID_X9_62_prime256v1;
+ } else {
+ len = DNS_KEY_ECDSA384SIZE;
+ group_nid = NID_secp384r1;
+ }
+
+ isc_buffer_remainingregion(data, &r);
+ if (r.length == 0)
+ return (ISC_R_SUCCESS);
+ if (r.length < len)
+ return (DST_R_INVALIDPUBLICKEY);
+
+ eckey = EC_KEY_new_by_curve_name(group_nid);
+ if (eckey == NULL)
+ return (dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+
+ buf[0] = POINT_CONVERSION_UNCOMPRESSED;
+ memmove(buf + 1, r.base, len);
+ cp = buf;
+ if (o2i_ECPublicKey(&eckey,
+ (const unsigned char **) &cp,
+ (long) len + 1) == NULL)
+ DST_RET (dst__openssl_toresult(DST_R_INVALIDPUBLICKEY));
+ if (EC_KEY_check_key(eckey) != 1)
+ DST_RET (dst__openssl_toresult(DST_R_INVALIDPUBLICKEY));
+
+ pkey = EVP_PKEY_new();
+ if (pkey == NULL)
+ DST_RET (ISC_R_NOMEMORY);
+ if (!EVP_PKEY_set1_EC_KEY(pkey, eckey)) {
+ EVP_PKEY_free(pkey);
+ DST_RET (dst__openssl_toresult(ISC_R_FAILURE));
+ }
+
+ isc_buffer_forward(data, len);
+ key->keydata.pkey = pkey;
+ key->key_size = len * 4;
+ ret = ISC_R_SUCCESS;
+
+ err:
+ if (eckey != NULL)
+ EC_KEY_free(eckey);
+ return (ret);
+}
+
+static isc_result_t
+opensslecdsa_tofile(const dst_key_t *key, const char *directory) {
+ isc_result_t ret;
+ EVP_PKEY *pkey;
+ EC_KEY *eckey = NULL;
+ const BIGNUM *privkey;
+ dst_private_t priv;
+ unsigned char *buf = NULL;
+
+ if (key->keydata.pkey == NULL)
+ return (DST_R_NULLKEY);
+
+ if (key->external) {
+ priv.nelements = 0;
+ return (dst__privstruct_writefile(key, &priv, directory));
+ }
+
+ pkey = key->keydata.pkey;
+ eckey = EVP_PKEY_get1_EC_KEY(pkey);
+ if (eckey == NULL)
+ return (dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+ privkey = EC_KEY_get0_private_key(eckey);
+ if (privkey == NULL)
+ DST_RET (ISC_R_FAILURE);
+
+ buf = isc_mem_get(key->mctx, BN_num_bytes(privkey));
+ if (buf == NULL)
+ DST_RET (ISC_R_NOMEMORY);
+
+ priv.elements[0].tag = TAG_ECDSA_PRIVATEKEY;
+ priv.elements[0].length = BN_num_bytes(privkey);
+ BN_bn2bin(privkey, buf);
+ priv.elements[0].data = buf;
+ priv.nelements = 1;
+ ret = dst__privstruct_writefile(key, &priv, directory);
+
+ err:
+ if (eckey != NULL)
+ EC_KEY_free(eckey);
+ if (buf != NULL)
+ isc_mem_put(key->mctx, buf, BN_num_bytes(privkey));
+ return (ret);
+}
+
+static isc_result_t
+ecdsa_check(EC_KEY *eckey, dst_key_t *pub)
+{
+ isc_result_t ret = ISC_R_FAILURE;
+ EVP_PKEY *pkey;
+ EC_KEY *pubeckey = NULL;
+ const EC_POINT *pubkey;
+
+ if (pub == NULL)
+ return (ISC_R_SUCCESS);
+ pkey = pub->keydata.pkey;
+ if (pkey == NULL)
+ return (ISC_R_SUCCESS);
+ pubeckey = EVP_PKEY_get1_EC_KEY(pkey);
+ if (pubeckey == NULL)
+ return (ISC_R_SUCCESS);
+ pubkey = EC_KEY_get0_public_key(pubeckey);
+ if (pubkey == NULL)
+ DST_RET (ISC_R_SUCCESS);
+ if (EC_KEY_set_public_key(eckey, pubkey) != 1)
+ DST_RET (ISC_R_SUCCESS);
+ if (EC_KEY_check_key(eckey) == 1)
+ DST_RET (ISC_R_SUCCESS);
+
+ err:
+ if (pubeckey != NULL)
+ EC_KEY_free(pubeckey);
+ return (ret);
+}
+
+static isc_result_t
+opensslecdsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
+ dst_private_t priv;
+ isc_result_t ret;
+ EVP_PKEY *pkey;
+ EC_KEY *eckey = NULL;
+ BIGNUM *privkey = NULL;
+ int group_nid;
+ isc_mem_t *mctx = key->mctx;
+
+ REQUIRE(key->key_alg == DST_ALG_ECDSA256 ||
+ key->key_alg == DST_ALG_ECDSA384);
+
+ /* read private key file */
+ ret = dst__privstruct_parse(key, DST_ALG_ECDSA256, lexer, mctx, &priv);
+ if (ret != ISC_R_SUCCESS)
+ goto err;
+
+ if (key->external) {
+ if (priv.nelements != 0)
+ DST_RET(DST_R_INVALIDPRIVATEKEY);
+ if (pub == NULL)
+ DST_RET(DST_R_INVALIDPRIVATEKEY);
+ key->keydata.pkey = pub->keydata.pkey;
+ pub->keydata.pkey = NULL;
+ dst__privstruct_free(&priv, mctx);
+ isc_safe_memwipe(&priv, sizeof(priv));
+ return (ISC_R_SUCCESS);
+ }
+
+ if (key->key_alg == DST_ALG_ECDSA256)
+ group_nid = NID_X9_62_prime256v1;
+ else
+ group_nid = NID_secp384r1;
+
+ eckey = EC_KEY_new_by_curve_name(group_nid);
+ if (eckey == NULL)
+ return (dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+
+ privkey = BN_bin2bn(priv.elements[0].data,
+ priv.elements[0].length, NULL);
+ if (privkey == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ if (!EC_KEY_set_private_key(eckey, privkey))
+ DST_RET(ISC_R_NOMEMORY);
+ if (ecdsa_check(eckey, pub) != ISC_R_SUCCESS)
+ DST_RET(DST_R_INVALIDPRIVATEKEY);
+
+ pkey = EVP_PKEY_new();
+ if (pkey == NULL)
+ DST_RET (ISC_R_NOMEMORY);
+ if (!EVP_PKEY_set1_EC_KEY(pkey, eckey)) {
+ EVP_PKEY_free(pkey);
+ DST_RET (dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+ }
+ key->keydata.pkey = pkey;
+ if (key->key_alg == DST_ALG_ECDSA256)
+ key->key_size = DNS_KEY_ECDSA256SIZE * 4;
+ else
+ key->key_size = DNS_KEY_ECDSA384SIZE * 4;
+ ret = ISC_R_SUCCESS;
+
+ err:
+ if (privkey != NULL)
+ BN_clear_free(privkey);
+ if (eckey != NULL)
+ EC_KEY_free(eckey);
+ dst__privstruct_free(&priv, mctx);
+ isc_safe_memwipe(&priv, sizeof(priv));
+ return (ret);
+}
+
+static dst_func_t opensslecdsa_functions = {
+ opensslecdsa_createctx,
+ NULL, /*%< createctx2 */
+ opensslecdsa_destroyctx,
+ opensslecdsa_adddata,
+ opensslecdsa_sign,
+ opensslecdsa_verify,
+ NULL, /*%< verify2 */
+ NULL, /*%< computesecret */
+ opensslecdsa_compare,
+ NULL, /*%< paramcompare */
+ opensslecdsa_generate,
+ opensslecdsa_isprivate,
+ opensslecdsa_destroy,
+ opensslecdsa_todns,
+ opensslecdsa_fromdns,
+ opensslecdsa_tofile,
+ opensslecdsa_parse,
+ NULL, /*%< cleanup */
+ NULL, /*%< fromlabel */
+ NULL, /*%< dump */
+ NULL, /*%< restore */
+};
+
+isc_result_t
+dst__opensslecdsa_init(dst_func_t **funcp) {
+ REQUIRE(funcp != NULL);
+ if (*funcp == NULL)
+ *funcp = &opensslecdsa_functions;
+ return (ISC_R_SUCCESS);
+}
+
+#else /* HAVE_OPENSSL_ECDSA */
+
+#include <isc/util.h>
+
+EMPTY_TRANSLATION_UNIT
+
+#endif /* HAVE_OPENSSL_ECDSA */
+/*! \file */
diff --git a/lib/dns/openssleddsa_link.c b/lib/dns/openssleddsa_link.c
new file mode 100644
index 0000000..b76fac9
--- /dev/null
+++ b/lib/dns/openssleddsa_link.c
@@ -0,0 +1,676 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <config.h>
+
+#if defined(OPENSSL) && \
+ (defined(HAVE_OPENSSL_ED25519) || defined(HAVE_OPENSSL_ED448))
+
+#include <stdbool.h>
+
+#include <isc/entropy.h>
+#include <isc/mem.h>
+#include <isc/safe.h>
+#include <isc/sha2.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dns/keyvalues.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/evp.h>
+#include <openssl/x509.h>
+
+#ifndef NID_ED25519
+#error "Ed25519 group is not known (NID_ED25519)"
+#endif
+#ifndef NID_ED448
+#error "Ed448 group is not known (NID_ED448)"
+#endif
+
+#define DST_RET(a) {ret = a; goto err;}
+
+/* OpenSSL doesn't provide direct access to key values */
+
+#define PUBPREFIXLEN 12
+
+static const unsigned char ed25519_pub_prefix[] = {
+ 0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65,
+ 0x70, 0x03, 0x21, 0x00
+};
+
+static EVP_PKEY *pub_ed25519_to_ossl(const unsigned char *key)
+{
+ unsigned char buf[PUBPREFIXLEN + DNS_KEY_ED25519SIZE];
+ const unsigned char *p;
+
+ memmove(buf, ed25519_pub_prefix, PUBPREFIXLEN);
+ memmove(buf + PUBPREFIXLEN, key, DNS_KEY_ED25519SIZE);
+ p = buf;
+ return (d2i_PUBKEY(NULL, &p, PUBPREFIXLEN + DNS_KEY_ED25519SIZE));
+}
+
+static isc_result_t pub_ed25519_from_ossl(EVP_PKEY *pkey,
+ unsigned char *key)
+{
+ unsigned char buf[PUBPREFIXLEN + DNS_KEY_ED25519SIZE];
+ unsigned char *p;
+ int len;
+
+ len = i2d_PUBKEY(pkey, NULL);
+ if ((len <= DNS_KEY_ED25519SIZE) ||
+ (len > PUBPREFIXLEN + DNS_KEY_ED25519SIZE))
+ return (DST_R_OPENSSLFAILURE);
+ p = buf;
+ len = i2d_PUBKEY(pkey, &p);
+ if ((len <= DNS_KEY_ED25519SIZE) ||
+ (len > PUBPREFIXLEN + DNS_KEY_ED25519SIZE))
+ return (DST_R_OPENSSLFAILURE);
+ memmove(key, buf + len - DNS_KEY_ED25519SIZE, DNS_KEY_ED25519SIZE);
+ return (ISC_R_SUCCESS);
+}
+
+static const unsigned char ed448_pub_prefix[] = {
+ 0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65,
+ 0x71, 0x03, 0x21, 0x00
+};
+
+static EVP_PKEY *pub_ed448_to_ossl(const unsigned char *key)
+{
+ unsigned char buf[PUBPREFIXLEN + DNS_KEY_ED448SIZE];
+ const unsigned char *p;
+
+ memmove(buf, ed448_pub_prefix, PUBPREFIXLEN);
+ memmove(buf + PUBPREFIXLEN, key, DNS_KEY_ED448SIZE);
+ p = buf;
+ return (d2i_PUBKEY(NULL, &p, PUBPREFIXLEN + DNS_KEY_ED448SIZE));
+}
+
+static isc_result_t pub_ed448_from_ossl(EVP_PKEY *pkey,
+ unsigned char *key)
+{
+ unsigned char buf[PUBPREFIXLEN + DNS_KEY_ED448SIZE];
+ unsigned char *p;
+ int len;
+
+ len = i2d_PUBKEY(pkey, NULL);
+ if ((len <= DNS_KEY_ED448SIZE) ||
+ (len > PUBPREFIXLEN + DNS_KEY_ED448SIZE))
+ return (DST_R_OPENSSLFAILURE);
+ p = buf;
+ len = i2d_PUBKEY(pkey, &p);
+ if ((len <= DNS_KEY_ED448SIZE) ||
+ (len > PUBPREFIXLEN + DNS_KEY_ED448SIZE))
+ return (DST_R_OPENSSLFAILURE);
+ memmove(key, buf + len - DNS_KEY_ED448SIZE, DNS_KEY_ED448SIZE);
+ return (ISC_R_SUCCESS);
+}
+
+#define PRIVPREFIXLEN 16
+
+static const unsigned char ed25519_priv_prefix[] = {
+ 0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06,
+ 0x03, 0x2b, 0x65, 0x70, 0x04, 0x22, 0x04, 0x20
+};
+
+static EVP_PKEY *priv_ed25519_to_ossl(const unsigned char *key)
+{
+ unsigned char buf[PRIVPREFIXLEN + DNS_KEY_ED25519SIZE];
+ const unsigned char *p;
+
+ memmove(buf, ed25519_priv_prefix, PRIVPREFIXLEN);
+ memmove(buf + PRIVPREFIXLEN, key, DNS_KEY_ED25519SIZE);
+ p = buf;
+ return (d2i_PrivateKey(NID_ED25519, NULL, &p,
+ PRIVPREFIXLEN + DNS_KEY_ED25519SIZE));
+}
+
+static isc_result_t priv_ed25519_from_ossl(EVP_PKEY *pkey,
+ unsigned char *key)
+{
+ unsigned char buf[PRIVPREFIXLEN + DNS_KEY_ED25519SIZE];
+ unsigned char *p;
+ int len;
+
+ len = i2d_PrivateKey(pkey, NULL);
+ if ((len <= DNS_KEY_ED25519SIZE) ||
+ (len > PRIVPREFIXLEN + DNS_KEY_ED25519SIZE))
+ return (DST_R_OPENSSLFAILURE);
+ p = buf;
+ len = i2d_PrivateKey(pkey, &p);
+ if ((len <= DNS_KEY_ED25519SIZE) ||
+ (len > PRIVPREFIXLEN + DNS_KEY_ED25519SIZE))
+ return (DST_R_OPENSSLFAILURE);
+ memmove(key, buf + len - DNS_KEY_ED25519SIZE, DNS_KEY_ED25519SIZE);
+ return (ISC_R_SUCCESS);
+}
+
+static const unsigned char ed448_priv_prefix[] = {
+ 0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06,
+ 0x03, 0x2b, 0x65, 0x71, 0x04, 0x22, 0x04, 0x20
+};
+
+static EVP_PKEY *priv_ed448_to_ossl(const unsigned char *key)
+{
+ unsigned char buf[PRIVPREFIXLEN + DNS_KEY_ED448SIZE];
+ const unsigned char *p;
+
+ memmove(buf, ed448_priv_prefix, PRIVPREFIXLEN);
+ memmove(buf + PRIVPREFIXLEN, key, DNS_KEY_ED448SIZE);
+ p = buf;
+ return (d2i_PrivateKey(NID_ED448, NULL, &p,
+ PRIVPREFIXLEN + DNS_KEY_ED448SIZE));
+}
+
+static isc_result_t priv_ed448_from_ossl(EVP_PKEY *pkey,
+ unsigned char *key)
+{
+ unsigned char buf[PRIVPREFIXLEN + DNS_KEY_ED448SIZE];
+ unsigned char *p;
+ int len;
+
+ len = i2d_PrivateKey(pkey, NULL);
+ if ((len <= DNS_KEY_ED448SIZE) ||
+ (len > PRIVPREFIXLEN + DNS_KEY_ED448SIZE))
+ return (DST_R_OPENSSLFAILURE);
+ p = buf;
+ len = i2d_PrivateKey(pkey, &p);
+ if ((len <= DNS_KEY_ED448SIZE) ||
+ (len > PRIVPREFIXLEN + DNS_KEY_ED448SIZE))
+ return (DST_R_OPENSSLFAILURE);
+ memmove(key, buf + len - DNS_KEY_ED448SIZE, DNS_KEY_ED448SIZE);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t openssleddsa_todns(const dst_key_t *key,
+ isc_buffer_t *data);
+
+static isc_result_t
+openssleddsa_createctx(dst_key_t *key, dst_context_t *dctx) {
+ isc_buffer_t *buf = NULL;
+ isc_result_t result;
+
+ UNUSED(key);
+ REQUIRE(dctx->key->key_alg == DST_ALG_ED25519 ||
+ dctx->key->key_alg == DST_ALG_ED448);
+
+ result = isc_buffer_allocate(dctx->mctx, &buf, 64);
+ dctx->ctxdata.generic = buf;
+
+ return (result);
+}
+
+static void
+openssleddsa_destroyctx(dst_context_t *dctx) {
+ isc_buffer_t *buf = (isc_buffer_t *) dctx->ctxdata.generic;
+
+ REQUIRE(dctx->key->key_alg == DST_ALG_ED25519 ||
+ dctx->key->key_alg == DST_ALG_ED448);
+ if (buf != NULL)
+ isc_buffer_free(&buf);
+ dctx->ctxdata.generic = NULL;
+}
+
+static isc_result_t
+openssleddsa_adddata(dst_context_t *dctx, const isc_region_t *data) {
+ isc_buffer_t *buf = (isc_buffer_t *) dctx->ctxdata.generic;
+ isc_buffer_t *nbuf = NULL;
+ isc_region_t r;
+ unsigned int length;
+ isc_result_t result;
+
+ REQUIRE(dctx->key->key_alg == DST_ALG_ED25519 ||
+ dctx->key->key_alg == DST_ALG_ED448);
+
+ result = isc_buffer_copyregion(buf, data);
+ if (result == ISC_R_SUCCESS)
+ return (ISC_R_SUCCESS);
+
+ length = isc_buffer_length(buf) + data->length + 64;
+ result = isc_buffer_allocate(dctx->mctx, &nbuf, length);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ isc_buffer_usedregion(buf, &r);
+ (void) isc_buffer_copyregion(nbuf, &r);
+ (void) isc_buffer_copyregion(nbuf, data);
+ isc_buffer_free(&buf);
+ dctx->ctxdata.generic = nbuf;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+openssleddsa_sign(dst_context_t *dctx, isc_buffer_t *sig) {
+ isc_result_t ret;
+ dst_key_t *key = dctx->key;
+ isc_region_t tbsreg;
+ isc_region_t sigreg;
+ EVP_PKEY *pkey = key->keydata.pkey;
+ EVP_MD_CTX* ctx = EVP_MD_CTX_new();
+ isc_buffer_t *buf = (isc_buffer_t *) dctx->ctxdata.generic;
+ size_t siglen;
+
+ REQUIRE(key->key_alg == DST_ALG_ED25519 ||
+ key->key_alg == DST_ALG_ED448);
+
+ if (ctx == NULL)
+ return (ISC_R_NOMEMORY);
+
+ if (key->key_alg == DST_ALG_ED25519)
+ siglen = DNS_SIG_ED25519SIZE;
+ else
+ siglen = DNS_SIG_ED448SIZE;
+
+ isc_buffer_availableregion(sig, &sigreg);
+ if (sigreg.length < (unsigned int) siglen)
+ DST_RET(ISC_R_NOSPACE);
+
+ isc_buffer_usedregion(buf, &tbsreg);
+
+ if (!EVP_DigestSignInit(ctx, NULL, NULL, NULL, pkey))
+ DST_RET(dst__openssl_toresult3(dctx->category,
+ "EVP_DigestSignInit",
+ ISC_R_FAILURE));
+ if (!EVP_DigestSign(ctx, sigreg.base, &siglen,
+ tbsreg.base, tbsreg.length))
+ DST_RET(dst__openssl_toresult3(dctx->category,
+ "EVP_DigestSign",
+ DST_R_SIGNFAILURE));
+ isc_buffer_add(sig, (unsigned int) siglen);
+ ret = ISC_R_SUCCESS;
+
+ err:
+ if (ctx != NULL)
+ EVP_MD_CTX_free(ctx);
+ isc_buffer_free(&buf);
+ dctx->ctxdata.generic = NULL;
+
+ return (ret);
+}
+
+static isc_result_t
+openssleddsa_verify(dst_context_t *dctx, const isc_region_t *sig) {
+ isc_result_t ret;
+ dst_key_t *key = dctx->key;
+ int status;
+ isc_region_t tbsreg;
+ EVP_PKEY *pkey = key->keydata.pkey;
+ EVP_MD_CTX* ctx = EVP_MD_CTX_new();
+ isc_buffer_t *buf = (isc_buffer_t *) dctx->ctxdata.generic;
+ unsigned int siglen;
+
+ REQUIRE(key->key_alg == DST_ALG_ED25519 ||
+ key->key_alg == DST_ALG_ED448);
+
+ if (ctx == NULL)
+ return (ISC_R_NOMEMORY);
+
+ if (key->key_alg == DST_ALG_ED25519)
+ siglen = DNS_SIG_ED25519SIZE;
+ else
+ siglen = DNS_SIG_ED448SIZE;
+
+ if (sig->length != siglen)
+ return (DST_R_VERIFYFAILURE);
+
+ isc_buffer_usedregion(buf, &tbsreg);
+
+ if (!EVP_DigestVerifyInit(ctx, NULL, NULL, NULL, pkey))
+ DST_RET(dst__openssl_toresult3(dctx->category,
+ "EVP_DigestVerifyInit",
+ ISC_R_FAILURE));
+
+ status = EVP_DigestVerify(ctx, sig->base, siglen,
+ tbsreg.base, tbsreg.length);
+
+ switch (status) {
+ case 1:
+ ret = ISC_R_SUCCESS;
+ break;
+ case 0:
+ ret = dst__openssl_toresult(DST_R_VERIFYFAILURE);
+ break;
+ default:
+ ret = dst__openssl_toresult3(dctx->category,
+ "EVP_DigestVerify",
+ DST_R_VERIFYFAILURE);
+ break;
+ }
+
+ err:
+ if (ctx != NULL)
+ EVP_MD_CTX_free(ctx);
+ isc_buffer_free(&buf);
+ dctx->ctxdata.generic = NULL;
+
+ return (ret);
+}
+
+static bool
+openssleddsa_compare(const dst_key_t *key1, const dst_key_t *key2) {
+ int status;
+ EVP_PKEY *pkey1 = key1->keydata.pkey;
+ EVP_PKEY *pkey2 = key2->keydata.pkey;
+
+ if (pkey1 == NULL && pkey2 == NULL)
+ return (true);
+ else if (pkey1 == NULL || pkey2 == NULL)
+ return (false);
+
+ status = EVP_PKEY_cmp(pkey1, pkey2);
+ if (status == 1)
+ return (true);
+ return (false);
+}
+
+static isc_result_t
+openssleddsa_generate(dst_key_t *key, int unused, void (*callback)(int)) {
+ isc_result_t ret;
+ EVP_PKEY *pkey = NULL;
+ EVP_PKEY_CTX *ctx = NULL;
+ int nid, status;
+
+ REQUIRE(key->key_alg == DST_ALG_ED25519 ||
+ key->key_alg == DST_ALG_ED448);
+ UNUSED(unused);
+ UNUSED(callback);
+
+ if (key->key_alg == DST_ALG_ED25519) {
+ nid = NID_ED25519;
+ key->key_size = DNS_KEY_ED25519SIZE;
+ } else {
+ nid = NID_ED448;
+ key->key_size = DNS_KEY_ED448SIZE;
+ }
+
+ ctx = EVP_PKEY_CTX_new_id(nid, NULL);
+ if (ctx == NULL)
+ return (dst__openssl_toresult2("EVP_PKEY_CTX_new_id",
+ DST_R_OPENSSLFAILURE));
+
+ status = EVP_PKEY_keygen_init(ctx);
+ if (status != 1)
+ DST_RET (dst__openssl_toresult2("EVP_PKEY_keygen_init",
+ DST_R_OPENSSLFAILURE));
+
+ status = EVP_PKEY_keygen(ctx, &pkey);
+ if (status != 1)
+ DST_RET (dst__openssl_toresult2("EVP_PKEY_keygen",
+ DST_R_OPENSSLFAILURE));
+
+ key->keydata.pkey = pkey;
+ ret = ISC_R_SUCCESS;
+
+ err:
+ if (ctx != NULL)
+ EVP_PKEY_CTX_free(ctx);
+ return (ret);
+}
+
+static bool
+openssleddsa_isprivate(const dst_key_t *key) {
+ EVP_PKEY *pkey = key->keydata.pkey;
+ int len;
+ unsigned long err;
+
+ if (pkey == NULL)
+ return (false);
+
+ len = i2d_PrivateKey(pkey, NULL);
+ if (len > 0)
+ return (true);
+ /* can check if first error is EC_R_INVALID_PRIVATE_KEY */
+ while ((err = ERR_get_error()) != 0)
+ /**/;
+
+ return (false);
+}
+
+static void
+openssleddsa_destroy(dst_key_t *key) {
+ EVP_PKEY *pkey = key->keydata.pkey;
+
+ EVP_PKEY_free(pkey);
+ key->keydata.pkey = NULL;
+}
+
+static isc_result_t
+openssleddsa_todns(const dst_key_t *key, isc_buffer_t *data) {
+ EVP_PKEY *pkey = key->keydata.pkey;
+ isc_region_t r;
+ isc_result_t result;
+
+ REQUIRE(pkey != NULL);
+
+ pkey = key->keydata.pkey;
+ switch (key->key_alg) {
+ case DST_ALG_ED25519:
+ isc_buffer_availableregion(data, &r);
+ if (r.length < DNS_KEY_ED25519SIZE)
+ return (ISC_R_NOSPACE);
+ result = pub_ed25519_from_ossl(pkey, r.base);
+ if (result == ISC_R_SUCCESS)
+ isc_buffer_add(data, DNS_KEY_ED25519SIZE);
+ return (result);
+ case DST_ALG_ED448:
+ isc_buffer_availableregion(data, &r);
+ if (r.length < DNS_KEY_ED448SIZE)
+ return (ISC_R_NOSPACE);
+ result = pub_ed448_from_ossl(pkey, r.base);
+ if (result == ISC_R_SUCCESS)
+ isc_buffer_add(data, DNS_KEY_ED448SIZE);
+ return (result);
+ default:
+ INSIST(0);
+ }
+}
+
+static isc_result_t
+openssleddsa_fromdns(dst_key_t *key, isc_buffer_t *data) {
+ EVP_PKEY *pkey;
+ isc_region_t r;
+ unsigned int len;
+
+ REQUIRE(key->key_alg == DST_ALG_ED25519 ||
+ key->key_alg == DST_ALG_ED448);
+
+ isc_buffer_remainingregion(data, &r);
+ if (r.length == 0)
+ return (ISC_R_SUCCESS);
+ if (key->key_alg == DST_ALG_ED25519) {
+ len = DNS_KEY_ED25519SIZE;
+ if (r.length < len)
+ return (DST_R_INVALIDPUBLICKEY);
+ pkey = pub_ed25519_to_ossl(r.base);
+ } else {
+ len = DNS_KEY_ED448SIZE;
+ if (r.length < len)
+ return (DST_R_INVALIDPUBLICKEY);
+ pkey = pub_ed448_to_ossl(r.base);
+ }
+ if (pkey == NULL)
+ return (dst__openssl_toresult(ISC_R_FAILURE));
+ isc_buffer_forward(data, len);
+ key->keydata.pkey = pkey;
+ key->key_size = len;
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+openssleddsa_tofile(const dst_key_t *key, const char *directory) {
+ isc_result_t ret;
+ EVP_PKEY *pkey;
+ dst_private_t priv;
+ unsigned char *buf = NULL;
+ unsigned int len;
+
+ REQUIRE(key->key_alg == DST_ALG_ED25519 ||
+ key->key_alg == DST_ALG_ED448);
+
+ if (key->keydata.pkey == NULL)
+ return (DST_R_NULLKEY);
+
+ if (key->external) {
+ priv.nelements = 0;
+ return (dst__privstruct_writefile(key, &priv, directory));
+ }
+
+ pkey = key->keydata.pkey;
+ if (key->key_alg == DST_ALG_ED25519) {
+ len = DNS_KEY_ED25519SIZE;
+ buf = isc_mem_get(key->mctx, len);
+ if (buf == NULL)
+ return (ISC_R_NOMEMORY);
+ priv.elements[0].tag = TAG_EDDSA_PRIVATEKEY;
+ priv.elements[0].length = len;
+ ret = priv_ed25519_from_ossl(pkey, buf);
+ if (ret != ISC_R_SUCCESS)
+ DST_RET (dst__openssl_toresult(ret));
+ priv.elements[0].data = buf;
+ priv.nelements = 1;
+ ret = dst__privstruct_writefile(key, &priv, directory);
+ } else {
+ len = DNS_KEY_ED448SIZE;
+ buf = isc_mem_get(key->mctx, len);
+ if (buf == NULL)
+ return (ISC_R_NOMEMORY);
+ priv.elements[0].tag = TAG_EDDSA_PRIVATEKEY;
+ priv.elements[0].length = len;
+ ret = priv_ed448_from_ossl(pkey, buf);
+ if (ret != ISC_R_SUCCESS)
+ DST_RET (dst__openssl_toresult(ret));
+ priv.elements[0].data = buf;
+ priv.nelements = 1;
+ ret = dst__privstruct_writefile(key, &priv, directory);
+ }
+
+ err:
+ if (buf != NULL)
+ isc_mem_put(key->mctx, buf, len);
+ return (ret);
+}
+
+static isc_result_t
+eddsa_check(EVP_PKEY *privkey, dst_key_t *pub)
+{
+ EVP_PKEY *pkey;
+
+ if (pub == NULL)
+ return (ISC_R_SUCCESS);
+ pkey = pub->keydata.pkey;
+ if (pkey == NULL)
+ return (ISC_R_SUCCESS);
+ if (EVP_PKEY_cmp(privkey, pkey) == 1)
+ return (ISC_R_SUCCESS);
+ return (ISC_R_FAILURE);
+}
+
+static isc_result_t
+openssleddsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
+ dst_private_t priv;
+ isc_result_t ret;
+ EVP_PKEY *pkey = NULL;
+ unsigned int len;
+ isc_mem_t *mctx = key->mctx;
+
+ REQUIRE(key->key_alg == DST_ALG_ED25519 ||
+ key->key_alg == DST_ALG_ED448);
+
+ /* read private key file */
+ ret = dst__privstruct_parse(key, DST_ALG_ED25519, lexer, mctx, &priv);
+ if (ret != ISC_R_SUCCESS)
+ goto err;
+
+ if (key->external) {
+ if (priv.nelements != 0)
+ DST_RET(DST_R_INVALIDPRIVATEKEY);
+ if (pub == NULL)
+ DST_RET(DST_R_INVALIDPRIVATEKEY);
+ key->keydata.pkey = pub->keydata.pkey;
+ pub->keydata.pkey = NULL;
+ dst__privstruct_free(&priv, mctx);
+ isc_safe_memwipe(&priv, sizeof(priv));
+ return (ISC_R_SUCCESS);
+ }
+
+ if (key->key_alg == DST_ALG_ED25519) {
+ len = DNS_KEY_ED25519SIZE;
+ if (priv.elements[0].length < len)
+ DST_RET(DST_R_INVALIDPRIVATEKEY);
+ pkey = priv_ed25519_to_ossl(priv.elements[0].data);
+ } else {
+ len = DNS_KEY_ED448SIZE;
+ if (priv.elements[0].length < len)
+ DST_RET(DST_R_INVALIDPRIVATEKEY);
+ pkey = priv_ed448_to_ossl(priv.elements[0].data);
+ }
+ if (pkey == NULL)
+ DST_RET (dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+ if (eddsa_check(pkey, pub) != ISC_R_SUCCESS) {
+ EVP_PKEY_free(pkey);
+ DST_RET(DST_R_INVALIDPRIVATEKEY);
+ }
+ key->keydata.pkey = pkey;
+ key->key_size = len;
+ ret = ISC_R_SUCCESS;
+
+ err:
+ dst__privstruct_free(&priv, mctx);
+ isc_safe_memwipe(&priv, sizeof(priv));
+ return (ret);
+}
+
+static dst_func_t openssleddsa_functions = {
+ openssleddsa_createctx,
+ NULL, /*%< createctx2 */
+ openssleddsa_destroyctx,
+ openssleddsa_adddata,
+ openssleddsa_sign,
+ openssleddsa_verify,
+ NULL, /*%< verify2 */
+ NULL, /*%< computesecret */
+ openssleddsa_compare,
+ NULL, /*%< paramcompare */
+ openssleddsa_generate,
+ openssleddsa_isprivate,
+ openssleddsa_destroy,
+ openssleddsa_todns,
+ openssleddsa_fromdns,
+ openssleddsa_tofile,
+ openssleddsa_parse,
+ NULL, /*%< cleanup */
+ NULL, /*%< fromlabel */
+ NULL, /*%< dump */
+ NULL, /*%< restore */
+};
+
+isc_result_t
+dst__openssleddsa_init(dst_func_t **funcp) {
+ REQUIRE(funcp != NULL);
+ if (*funcp == NULL)
+ *funcp = &openssleddsa_functions;
+ return (ISC_R_SUCCESS);
+}
+
+#else /* HAVE_OPENSSL_EDxxx */
+
+#include <isc/util.h>
+
+EMPTY_TRANSLATION_UNIT
+
+#endif /* HAVE_OPENSSL_EDxxx */
+/*! \file */
diff --git a/lib/dns/opensslgost_link.c b/lib/dns/opensslgost_link.c
new file mode 100644
index 0000000..481b91c
--- /dev/null
+++ b/lib/dns/opensslgost_link.c
@@ -0,0 +1,630 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <config.h>
+
+#if defined(OPENSSL) && defined(HAVE_OPENSSL_GOST)
+
+#include <stdbool.h>
+
+#include <isc/entropy.h>
+#include <isc/mem.h>
+#include <isc/safe.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 "dst_gost.h"
+
+#include <openssl/err.h>
+#include <openssl/objects.h>
+#include <openssl/rsa.h>
+#include <openssl/engine.h>
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+#define EVP_MD_CTX_new() &(ctx->_ctx), EVP_MD_CTX_init(&(ctx->_ctx))
+#define EVP_MD_CTX_free(ptr) EVP_MD_CTX_cleanup(ptr)
+#endif
+
+static ENGINE *e = NULL;
+static const EVP_MD *opensslgost_digest;
+extern const EVP_MD *EVP_gost(void);
+
+const EVP_MD *EVP_gost(void) {
+ return (opensslgost_digest);
+}
+
+/* ISC methods */
+
+isc_result_t
+isc_gost_init(isc_gost_t *ctx) {
+ const EVP_MD *md;
+ int ret;
+
+ INSIST(ctx != NULL);
+
+ md = EVP_gost();
+ if (md == NULL)
+ return (DST_R_CRYPTOFAILURE);
+ ctx->ctx = EVP_MD_CTX_new();
+ if (ctx->ctx == NULL)
+ return (ISC_R_NOMEMORY);
+ ret = EVP_DigestInit(ctx->ctx, md);
+ if (ret != 1)
+ return (DST_R_CRYPTOFAILURE);
+ return (ISC_R_SUCCESS);
+}
+
+void
+isc_gost_invalidate(isc_gost_t *ctx) {
+ EVP_MD_CTX_free(ctx->ctx);
+ ctx->ctx = NULL;
+}
+
+isc_result_t
+isc_gost_update(isc_gost_t *ctx, const unsigned char *data,
+ unsigned int len)
+{
+ int ret;
+
+ INSIST(ctx != NULL);
+ INSIST(ctx->ctx != NULL);
+ INSIST(data != NULL);
+
+ ret = EVP_DigestUpdate(ctx->ctx, (const void *) data, (size_t) len);
+ if (ret != 1)
+ return (DST_R_CRYPTOFAILURE);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_gost_final(isc_gost_t *ctx, unsigned char *digest) {
+ int ret;
+
+ INSIST(ctx != NULL);
+ INSIST(ctx->ctx != NULL);
+ INSIST(digest != NULL);
+
+ ret = EVP_DigestFinal(ctx->ctx, digest, NULL);
+ EVP_MD_CTX_free(ctx->ctx);
+ ctx->ctx = NULL;
+ if (ret != 1)
+ return (DST_R_CRYPTOFAILURE);
+ return (ISC_R_SUCCESS);
+}
+
+/* DST methods */
+
+#define DST_RET(a) {ret = a; goto err;}
+
+static isc_result_t opensslgost_todns(const dst_key_t *key,
+ isc_buffer_t *data);
+
+static isc_result_t
+opensslgost_createctx(dst_key_t *key, dst_context_t *dctx) {
+ EVP_MD_CTX *evp_md_ctx;
+ const EVP_MD *md = EVP_gost();
+
+ UNUSED(key);
+
+ if (md == NULL)
+ return (DST_R_OPENSSLFAILURE);
+
+ evp_md_ctx = EVP_MD_CTX_create();
+ if (evp_md_ctx == NULL)
+ return (ISC_R_NOMEMORY);
+
+ if (!EVP_DigestInit_ex(evp_md_ctx, md, NULL)) {
+ EVP_MD_CTX_destroy(evp_md_ctx);
+ return (ISC_R_FAILURE);
+ }
+ dctx->ctxdata.evp_md_ctx = evp_md_ctx;
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+opensslgost_destroyctx(dst_context_t *dctx) {
+ 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;
+ }
+}
+
+static isc_result_t
+opensslgost_adddata(dst_context_t *dctx, const isc_region_t *data) {
+ 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);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+opensslgost_sign(dst_context_t *dctx, isc_buffer_t *sig) {
+ dst_key_t *key = dctx->key;
+ isc_region_t r;
+ unsigned int siglen = 0;
+ EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
+ EVP_PKEY *pkey = key->keydata.pkey;
+
+ isc_buffer_availableregion(sig, &r);
+
+ 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);
+
+ isc_buffer_add(sig, siglen);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+opensslgost_verify(dst_context_t *dctx, const isc_region_t *sig) {
+ dst_key_t *key = dctx->key;
+ int status = 0;
+ EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
+ EVP_PKEY *pkey = key->keydata.pkey;
+
+ status = EVP_VerifyFinal(evp_md_ctx, sig->base, sig->length, pkey);
+ switch (status) {
+ case 1:
+ return (ISC_R_SUCCESS);
+ case 0:
+ return (dst__openssl_toresult(DST_R_VERIFYFAILURE));
+ default:
+ return (dst__openssl_toresult3(dctx->category,
+ "EVP_VerifyFinal",
+ DST_R_VERIFYFAILURE));
+ }
+}
+
+static bool
+opensslgost_compare(const dst_key_t *key1, const dst_key_t *key2) {
+ EVP_PKEY *pkey1, *pkey2;
+
+ pkey1 = key1->keydata.pkey;
+ pkey2 = key2->keydata.pkey;
+
+ if (pkey1 == NULL && pkey2 == NULL)
+ return (true);
+ else if (pkey1 == NULL || pkey2 == NULL)
+ return (false);
+
+ if (EVP_PKEY_cmp(pkey1, pkey2) != 1)
+ return (false);
+ return (true);
+}
+
+static int
+progress_cb(EVP_PKEY_CTX *ctx)
+{
+ union {
+ void *dptr;
+ void (*fptr)(int);
+ } u;
+ int p;
+
+ u.dptr = EVP_PKEY_CTX_get_app_data(ctx);
+ p = EVP_PKEY_CTX_get_keygen_info(ctx, 0);
+ if (u.fptr != NULL)
+ u.fptr(p);
+ return (1);
+}
+
+static isc_result_t
+opensslgost_generate(dst_key_t *key, int unused, void (*callback)(int)) {
+ EVP_PKEY_CTX *ctx;
+ union {
+ void *dptr;
+ void (*fptr)(int);
+ } u;
+ EVP_PKEY *pkey = NULL;
+ isc_result_t ret;
+
+ UNUSED(unused);
+ ctx = EVP_PKEY_CTX_new_id(NID_id_GostR3410_2001, NULL);
+ if (ctx == NULL)
+ DST_RET(dst__openssl_toresult2("EVP_PKEY_CTX_new_id",
+ DST_R_OPENSSLFAILURE));
+ if (callback != NULL) {
+ u.fptr = callback;
+ EVP_PKEY_CTX_set_app_data(ctx, u.dptr);
+ EVP_PKEY_CTX_set_cb(ctx, &progress_cb);
+ }
+ if (EVP_PKEY_keygen_init(ctx) <= 0)
+ DST_RET(dst__openssl_toresult2("EVP_PKEY_keygen_init",
+ DST_R_OPENSSLFAILURE));
+ if (EVP_PKEY_CTX_ctrl_str(ctx, "paramset", "A") <= 0)
+ DST_RET(dst__openssl_toresult2("EVP_PKEY_CTX_ctrl_str",
+ DST_R_OPENSSLFAILURE));
+ if (EVP_PKEY_keygen(ctx, &pkey) <= 0)
+ DST_RET(dst__openssl_toresult2("EVP_PKEY_keygen",
+ DST_R_OPENSSLFAILURE));
+ key->keydata.pkey = pkey;
+ key->key_size = EVP_PKEY_bits(pkey);
+ EVP_PKEY_CTX_free(ctx);
+ return (ISC_R_SUCCESS);
+
+err:
+ if (pkey != NULL)
+ EVP_PKEY_free(pkey);
+ if (ctx != NULL)
+ EVP_PKEY_CTX_free(ctx);
+ return (ret);
+}
+
+static bool
+opensslgost_isprivate(const dst_key_t *key) {
+ EVP_PKEY *pkey = key->keydata.pkey;
+ EC_KEY *ec;
+
+ INSIST(pkey != NULL);
+
+ ec = EVP_PKEY_get0(pkey);
+ return (ec != NULL && EC_KEY_get0_private_key(ec) != NULL);
+}
+
+static void
+opensslgost_destroy(dst_key_t *key) {
+ EVP_PKEY *pkey = key->keydata.pkey;
+
+ EVP_PKEY_free(pkey);
+ key->keydata.pkey = NULL;
+}
+
+static const unsigned char gost_prefix[37] = {
+ 0x30, 0x63, 0x30, 0x1c, 0x06, 0x06, 0x2a, 0x85,
+ 0x03, 0x02, 0x02, 0x13, 0x30, 0x12, 0x06, 0x07,
+ 0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x01, 0x06,
+ 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1e, 0x01,
+ 0x03, 0x43, 0x00, 0x04, 0x40
+};
+
+static isc_result_t
+opensslgost_todns(const dst_key_t *key, isc_buffer_t *data) {
+ EVP_PKEY *pkey;
+ isc_region_t r;
+ unsigned char der[37 + 64], *p;
+ int len;
+
+ REQUIRE(key->keydata.pkey != NULL);
+
+ pkey = key->keydata.pkey;
+
+ isc_buffer_availableregion(data, &r);
+ if (r.length < 64)
+ return (ISC_R_NOSPACE);
+
+ p = der;
+ len = i2d_PUBKEY(pkey, &p);
+ INSIST(len == sizeof(der));
+ INSIST(isc_safe_memequal(gost_prefix, der, 37));
+ memmove(r.base, der + 37, 64);
+ isc_buffer_add(data, 64);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+opensslgost_fromdns(dst_key_t *key, isc_buffer_t *data) {
+ isc_region_t r;
+ EVP_PKEY *pkey = NULL;
+ unsigned char der[37 + 64];
+ const unsigned char *p;
+
+ isc_buffer_remainingregion(data, &r);
+ if (r.length == 0)
+ return (ISC_R_SUCCESS);
+
+ if (r.length != 64)
+ return (DST_R_INVALIDPUBLICKEY);
+ memmove(der, gost_prefix, 37);
+ memmove(der + 37, r.base, 64);
+ isc_buffer_forward(data, 64);
+
+ p = der;
+ if (d2i_PUBKEY(&pkey, &p, (long) sizeof(der)) == NULL)
+ return (dst__openssl_toresult2("d2i_PUBKEY",
+ DST_R_OPENSSLFAILURE));
+ key->keydata.pkey = pkey;
+ key->key_size = EVP_PKEY_bits(pkey);
+
+ return (ISC_R_SUCCESS);
+}
+
+#ifdef PREFER_GOSTASN1
+
+static isc_result_t
+opensslgost_tofile(const dst_key_t *key, const char *directory) {
+ EVP_PKEY *pkey;
+ dst_private_t priv;
+ isc_result_t result;
+ unsigned char *der, *p;
+ int len;
+
+ if (key->keydata.pkey == NULL)
+ return (DST_R_NULLKEY);
+
+ if (key->external) {
+ priv.nelements = 0;
+ return (dst__privstruct_writefile(key, &priv, directory));
+ }
+
+ pkey = key->keydata.pkey;
+
+ len = i2d_PrivateKey(pkey, NULL);
+ der = isc_mem_get(key->mctx, (size_t) len);
+ if (der == NULL)
+ return (ISC_R_NOMEMORY);
+
+ p = der;
+ if (i2d_PrivateKey(pkey, &p) != len) {
+ result = dst__openssl_toresult2("i2d_PrivateKey",
+ DST_R_OPENSSLFAILURE);
+ goto fail;
+ }
+
+ priv.elements[0].tag = TAG_GOST_PRIVASN1;
+ priv.elements[0].length = len;
+ priv.elements[0].data = der;
+ priv.nelements = 1;
+
+ result = dst__privstruct_writefile(key, &priv, directory);
+ fail:
+ if (der != NULL)
+ isc_mem_put(key->mctx, der, (size_t) len);
+ return (result);
+}
+
+#else
+
+static isc_result_t
+opensslgost_tofile(const dst_key_t *key, const char *directory) {
+ EVP_PKEY *pkey;
+ EC_KEY *eckey;
+ const BIGNUM *privkey;
+ dst_private_t priv;
+ isc_result_t ret;
+ unsigned char *buf = NULL;
+
+ if (key->keydata.pkey == NULL)
+ return (DST_R_NULLKEY);
+
+ if (key->external) {
+ priv.nelements = 0;
+ return (dst__privstruct_writefile(key, &priv, directory));
+ }
+
+ pkey = key->keydata.pkey;
+ eckey = EVP_PKEY_get0(pkey);
+ if (eckey == NULL)
+ return (dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+ privkey = EC_KEY_get0_private_key(eckey);
+ if (privkey == NULL)
+ return (ISC_R_FAILURE);
+
+ buf = isc_mem_get(key->mctx, BN_num_bytes(privkey));
+ if (buf == NULL)
+ return (ISC_R_NOMEMORY);
+
+ priv.elements[0].tag = TAG_GOST_PRIVRAW;
+ priv.elements[0].length = BN_num_bytes(privkey);
+ BN_bn2bin(privkey, buf);
+ priv.elements[0].data = buf;
+ priv.nelements = 1;
+
+ ret = dst__privstruct_writefile(key, &priv, directory);
+
+ if (buf != NULL)
+ isc_mem_put(key->mctx, buf, BN_num_bytes(privkey));
+ return (ret);
+}
+#endif
+
+static unsigned char gost_dummy_key[71] = {
+ 0x30, 0x45, 0x02, 0x01, 0x00, 0x30, 0x1c, 0x06,
+ 0x06, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x13, 0x30,
+ 0x12, 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02,
+ 0x23, 0x01, 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02,
+ 0x02, 0x1e, 0x01, 0x04, 0x22, 0x02, 0x20, 0x1b,
+ 0x3f, 0x94, 0xf7, 0x1a, 0x5f, 0x2f, 0xe7, 0xe5,
+ 0x74, 0x0b, 0x8c, 0xd4, 0xb7, 0x18, 0xdd, 0x65,
+ 0x68, 0x26, 0xd1, 0x54, 0xfb, 0x77, 0xba, 0x63,
+ 0x72, 0xd9, 0xf0, 0x63, 0x87, 0xe0, 0xd6
+};
+
+static isc_result_t
+opensslgost_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
+ dst_private_t priv;
+ isc_result_t ret;
+ isc_mem_t *mctx = key->mctx;
+ EVP_PKEY *pkey = NULL;
+ EC_KEY *eckey;
+ const EC_POINT *pubkey = NULL;
+ BIGNUM *privkey = NULL;
+ const unsigned char *p;
+
+ /* read private key file */
+ ret = dst__privstruct_parse(key, DST_ALG_ECCGOST, lexer, mctx, &priv);
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+
+ if (key->external) {
+ if (priv.nelements != 0)
+ DST_RET(DST_R_INVALIDPRIVATEKEY);
+ if (pub == NULL)
+ DST_RET(DST_R_INVALIDPRIVATEKEY);
+ key->keydata.pkey = pub->keydata.pkey;
+ pub->keydata.pkey = NULL;
+ key->key_size = pub->key_size;
+ dst__privstruct_free(&priv, mctx);
+ isc_safe_memwipe(&priv, sizeof(priv));
+ return (ISC_R_SUCCESS);
+ }
+
+ INSIST((priv.elements[0].tag == TAG_GOST_PRIVASN1) ||
+ (priv.elements[0].tag == TAG_GOST_PRIVRAW));
+
+ if (priv.elements[0].tag == TAG_GOST_PRIVASN1) {
+ p = priv.elements[0].data;
+ if (d2i_PrivateKey(NID_id_GostR3410_2001, &pkey, &p,
+ (long) priv.elements[0].length) == NULL)
+ DST_RET(dst__openssl_toresult2(
+ "d2i_PrivateKey",
+ DST_R_INVALIDPRIVATEKEY));
+ } else {
+ if ((pub != NULL) && (pub->keydata.pkey != NULL)) {
+ eckey = EVP_PKEY_get0(pub->keydata.pkey);
+ pubkey = EC_KEY_get0_public_key(eckey);
+ }
+
+ privkey = BN_bin2bn(priv.elements[0].data,
+ priv.elements[0].length, NULL);
+ if (privkey == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+
+ /* can't create directly the whole key */
+ p = gost_dummy_key;
+ if (d2i_PrivateKey(NID_id_GostR3410_2001, &pkey, &p,
+ (long) sizeof(gost_dummy_key)) == NULL)
+ DST_RET(dst__openssl_toresult2(
+ "d2i_PrivateKey",
+ DST_R_INVALIDPRIVATEKEY));
+
+ eckey = EVP_PKEY_get0(pkey);
+ if (eckey == NULL)
+ return (dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+ if (!EC_KEY_set_private_key(eckey, privkey))
+ DST_RET(ISC_R_NOMEMORY);
+
+ /* have to (re)set the public key */
+#ifdef notyet
+ (void) gost2001_compute_public(eckey);
+#else
+ if ((pubkey != NULL) && !EC_KEY_set_public_key(eckey, pubkey))
+ DST_RET(ISC_R_NOMEMORY);
+#endif
+ BN_clear_free(privkey);
+ privkey = NULL;
+ }
+ key->keydata.pkey = pkey;
+ key->key_size = EVP_PKEY_bits(pkey);
+ dst__privstruct_free(&priv, mctx);
+ isc_safe_memwipe(&priv, sizeof(priv));
+ return (ISC_R_SUCCESS);
+
+ err:
+ if (privkey != NULL)
+ BN_clear_free(privkey);
+ if (pkey != NULL)
+ EVP_PKEY_free(pkey);
+ opensslgost_destroy(key);
+ dst__privstruct_free(&priv, mctx);
+ isc_safe_memwipe(&priv, sizeof(priv));
+ return (ret);
+}
+
+static void
+opensslgost_cleanup(void) {
+ if (e != NULL) {
+ ENGINE_finish(e);
+ ENGINE_free(e);
+ e = NULL;
+ }
+}
+
+static dst_func_t opensslgost_functions = {
+ opensslgost_createctx,
+ NULL, /*%< createctx2 */
+ opensslgost_destroyctx,
+ opensslgost_adddata,
+ opensslgost_sign,
+ opensslgost_verify,
+ NULL, /*%< verify2 */
+ NULL, /*%< computesecret */
+ opensslgost_compare,
+ NULL, /*%< paramcompare */
+ opensslgost_generate,
+ opensslgost_isprivate,
+ opensslgost_destroy,
+ opensslgost_todns,
+ opensslgost_fromdns,
+ opensslgost_tofile,
+ opensslgost_parse,
+ opensslgost_cleanup,
+ NULL, /*%< fromlabel */
+ NULL, /*%< dump */
+ NULL /*%< restore */
+};
+
+isc_result_t
+dst__opensslgost_init(dst_func_t **funcp) {
+ isc_result_t ret;
+
+ REQUIRE(funcp != NULL);
+
+ /* check if the gost engine works properly */
+ e = ENGINE_by_id("gost");
+ if (e == NULL)
+ return (dst__openssl_toresult2("ENGINE_by_id",
+ DST_R_OPENSSLFAILURE));
+ if (ENGINE_init(e) <= 0) {
+ ENGINE_free(e);
+ e = NULL;
+ return (dst__openssl_toresult2("ENGINE_init",
+ DST_R_OPENSSLFAILURE));
+ }
+ /* better than to rely on digest_gost symbol */
+ opensslgost_digest = ENGINE_get_digest(e, NID_id_GostR3411_94);
+ if (opensslgost_digest == NULL)
+ DST_RET(dst__openssl_toresult2("ENGINE_get_digest",
+ DST_R_OPENSSLFAILURE));
+ /* from openssl.cnf */
+ if (ENGINE_register_pkey_asn1_meths(e) <= 0)
+ DST_RET(dst__openssl_toresult2(
+ "ENGINE_register_pkey_asn1_meths",
+ DST_R_OPENSSLFAILURE));
+ if (ENGINE_ctrl_cmd_string(e,
+ "CRYPT_PARAMS",
+ "id-Gost28147-89-CryptoPro-A-ParamSet",
+ 0) <= 0)
+ DST_RET(dst__openssl_toresult2("ENGINE_ctrl_cmd_string",
+ DST_R_OPENSSLFAILURE));
+
+ if (*funcp == NULL)
+ *funcp = &opensslgost_functions;
+ return (ISC_R_SUCCESS);
+
+ err:
+ ENGINE_finish(e);
+ ENGINE_free(e);
+ e = NULL;
+ return (ret);
+}
+
+#else /* HAVE_OPENSSL_GOST */
+
+#include <isc/util.h>
+
+EMPTY_TRANSLATION_UNIT
+
+#endif /* HAVE_OPENSSL_GOST */
+/*! \file */
diff --git a/lib/dns/opensslrsa_link.c b/lib/dns/opensslrsa_link.c
new file mode 100644
index 0000000..c03fd72
--- /dev/null
+++ b/lib/dns/opensslrsa_link.c
@@ -0,0 +1,1830 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifdef OPENSSL
+#include <config.h>
+
+#ifndef USE_EVP
+#if !defined(HAVE_EVP_SHA256) || !defined(HAVE_EVP_SHA512)
+#define USE_EVP 0
+#else
+#define USE_EVP 1
+#endif
+#endif
+
+#include <stdbool.h>
+
+#include <isc/entropy.h>
+#include <isc/md5.h>
+#include <isc/mem.h>
+#include <isc/safe.h>
+#include <isc/sha1.h>
+#include <isc/sha2.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <pk11/site.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
+#if !defined(OPENSSL_NO_ENGINE)
+#include <openssl/engine.h>
+#endif
+
+/*
+ * Limit the size of public exponents.
+ */
+#ifndef RSA_MAX_PUBEXP_BITS
+#define RSA_MAX_PUBEXP_BITS 35
+#endif
+
+/*
+ * 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 && \
+ OPENSSL_VERSION_NUMBER < 0x10002000L) || \
+ OPENSSL_VERSION_NUMBER >= 0x1000205fL)
+#error Please upgrade OpenSSL to 0.9.8d/0.9.7l or greater.
+#endif
+#endif
+
+
+ /*
+ * XXXMPA Temporarily disable RSA_BLINDING as it requires
+ * good quality random data that cannot currently be guaranteed.
+ * 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 OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+#if 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
+#else
+#define SET_FLAGS(rsa) \
+ do { \
+ RSA_clear_flags(rsa, RSA_FLAG_BLINDING); \
+ RSA_set_flags(rsa, RSA_FLAG_NO_BLINDING); \
+ } while (0)
+#endif
+#define DST_RET(a) {ret = a; goto err;}
+
+#if !defined(HAVE_RSA_SET0_KEY)
+/* From OpenSSL 1.1.0 */
+static int
+RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) {
+
+ /*
+ * If the fields n and e in r are NULL, the corresponding input
+ * parameters MUST be non-NULL for n and e. d may be
+ * left NULL (in case only the public key is used).
+ */
+ if ((r->n == NULL && n == NULL) || (r->e == NULL && e == NULL)) {
+ return 0;
+ }
+
+ if (n != NULL) {
+ BN_free(r->n);
+ r->n = n;
+ }
+ if (e != NULL) {
+ BN_free(r->e);
+ r->e = e;
+ }
+ if (d != NULL) {
+ BN_free(r->d);
+ r->d = d;
+ }
+
+ return 1;
+}
+
+static int
+RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q) {
+
+ /*
+ * If the fields p and q in r are NULL, the corresponding input
+ * parameters MUST be non-NULL.
+ */
+ if ((r->p == NULL && p == NULL) || (r->q == NULL && q == NULL)) {
+ return 0;
+ }
+
+ if (p != NULL) {
+ BN_free(r->p);
+ r->p = p;
+ }
+ if (q != NULL) {
+ BN_free(r->q);
+ r->q = q;
+ }
+
+ return 1;
+}
+
+static int
+RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp) {
+ /*
+ * If the fields dmp1, dmq1 and iqmp in r are NULL, the
+ * corresponding input parameters MUST be non-NULL.
+ */
+ if ((r->dmp1 == NULL && dmp1 == NULL) ||
+ (r->dmq1 == NULL && dmq1 == NULL) ||
+ (r->iqmp == NULL && iqmp == NULL))
+ {
+ return 0;
+ }
+
+ if (dmp1 != NULL) {
+ BN_free(r->dmp1);
+ r->dmp1 = dmp1;
+ }
+ if (dmq1 != NULL) {
+ BN_free(r->dmq1);
+ r->dmq1 = dmq1;
+ }
+ if (iqmp != NULL) {
+ BN_free(r->iqmp);
+ r->iqmp = iqmp;
+ }
+
+ return 1;
+}
+
+static void
+RSA_get0_key(const RSA *r,
+ const BIGNUM **n, const BIGNUM **e, const BIGNUM **d)
+{
+ if (n != NULL) {
+ *n = r->n;
+ }
+ if (e != NULL) {
+ *e = r->e;
+ }
+ if (d != NULL) {
+ *d = r->d;
+ }
+}
+
+static void
+RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q) {
+ if (p != NULL) {
+ *p = r->p;
+ }
+ if (q != NULL) {
+ *q = r->q;
+ }
+}
+
+static void
+RSA_get0_crt_params(const RSA *r, const BIGNUM **dmp1, const BIGNUM **dmq1,
+ const BIGNUM **iqmp)
+{
+ if (dmp1 != NULL) {
+ *dmp1 = r->dmp1;
+ }
+ if (dmq1 != NULL) {
+ *dmq1 = r->dmq1;
+ }
+ if (iqmp != NULL) {
+ *iqmp = r->iqmp;
+ }
+}
+
+static int
+RSA_test_flags(const RSA *r, int flags) {
+ return (r->flags & flags);
+}
+
+#endif
+
+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 = NULL;
+#endif
+
+ UNUSED(key);
+#ifndef PK11_MD5_DISABLE
+ REQUIRE(dctx->key->key_alg == DST_ALG_RSAMD5 ||
+ dctx->key->key_alg == DST_ALG_RSASHA1 ||
+ dctx->key->key_alg == DST_ALG_NSEC3RSASHA1 ||
+ dctx->key->key_alg == DST_ALG_RSASHA256 ||
+ dctx->key->key_alg == DST_ALG_RSASHA512);
+#else
+ REQUIRE(dctx->key->key_alg == DST_ALG_RSASHA1 ||
+ dctx->key->key_alg == DST_ALG_NSEC3RSASHA1 ||
+ dctx->key->key_alg == DST_ALG_RSASHA256 ||
+ dctx->key->key_alg == DST_ALG_RSASHA512);
+#endif
+
+ /*
+ * Reject incorrect RSA key lengths.
+ */
+ switch (dctx->key->key_alg) {
+ case DST_ALG_RSAMD5:
+ case DST_ALG_RSASHA1:
+ case DST_ALG_NSEC3RSASHA1:
+ /* From RFC 3110 */
+ if (dctx->key->key_size > 4096)
+ return (ISC_R_FAILURE);
+ break;
+ case DST_ALG_RSASHA256:
+ /* From RFC 5702 */
+ if ((dctx->key->key_size < 512) ||
+ (dctx->key->key_size > 4096))
+ return (ISC_R_FAILURE);
+ break;
+ case DST_ALG_RSASHA512:
+ /* From RFC 5702 */
+ if ((dctx->key->key_size < 1024) ||
+ (dctx->key->key_size > 4096))
+ return (ISC_R_FAILURE);
+ break;
+ default:
+ INSIST(0);
+ }
+
+#if USE_EVP
+ evp_md_ctx = EVP_MD_CTX_create();
+ if (evp_md_ctx == NULL)
+ return (ISC_R_NOMEMORY);
+
+ switch (dctx->key->key_alg) {
+#ifndef PK11_MD5_DISABLE
+ case DST_ALG_RSAMD5:
+ type = EVP_md5(); /* MD5 + RSA */
+ break;
+#endif
+ case DST_ALG_RSASHA1:
+ case DST_ALG_NSEC3RSASHA1:
+ type = EVP_sha1(); /* SHA1 + RSA */
+ break;
+#ifdef HAVE_EVP_SHA256
+ case DST_ALG_RSASHA256:
+ type = EVP_sha256(); /* SHA256 + RSA */
+ break;
+#endif
+#ifdef HAVE_EVP_SHA512
+ case DST_ALG_RSASHA512:
+ type = EVP_sha512();
+ break;
+#endif
+ default:
+ INSIST(0);
+ }
+
+ if (!EVP_DigestInit_ex(evp_md_ctx, type, NULL)) {
+ EVP_MD_CTX_destroy(evp_md_ctx);
+ return (dst__openssl_toresult3(dctx->category,
+ "EVP_DigestInit_ex",
+ ISC_R_FAILURE));
+ }
+ dctx->ctxdata.evp_md_ctx = evp_md_ctx;
+#else
+ switch (dctx->key->key_alg) {
+#ifndef PK11_MD5_DISABLE
+ case 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;
+ }
+ break;
+#endif
+ case DST_ALG_RSASHA1:
+ case DST_ALG_NSEC3RSASHA1:
+ {
+ 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;
+ }
+ break;
+ case DST_ALG_RSASHA256:
+ {
+ isc_sha256_t *sha256ctx;
+
+ sha256ctx = isc_mem_get(dctx->mctx,
+ sizeof(isc_sha256_t));
+ if (sha256ctx == NULL)
+ return (ISC_R_NOMEMORY);
+ isc_sha256_init(sha256ctx);
+ dctx->ctxdata.sha256ctx = sha256ctx;
+ }
+ break;
+ case DST_ALG_RSASHA512:
+ {
+ isc_sha512_t *sha512ctx;
+
+ sha512ctx = isc_mem_get(dctx->mctx,
+ sizeof(isc_sha512_t));
+ if (sha512ctx == NULL)
+ return (ISC_R_NOMEMORY);
+ isc_sha512_init(sha512ctx);
+ dctx->ctxdata.sha512ctx = sha512ctx;
+ }
+ break;
+ default:
+ INSIST(0);
+ }
+#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
+
+#ifndef PK11_MD5_DISABLE
+ REQUIRE(dctx->key->key_alg == DST_ALG_RSAMD5 ||
+ dctx->key->key_alg == DST_ALG_RSASHA1 ||
+ dctx->key->key_alg == DST_ALG_NSEC3RSASHA1 ||
+ dctx->key->key_alg == DST_ALG_RSASHA256 ||
+ dctx->key->key_alg == DST_ALG_RSASHA512);
+#else
+ REQUIRE(dctx->key->key_alg == DST_ALG_RSASHA1 ||
+ dctx->key->key_alg == DST_ALG_NSEC3RSASHA1 ||
+ dctx->key->key_alg == DST_ALG_RSASHA256 ||
+ dctx->key->key_alg == DST_ALG_RSASHA512);
+#endif
+
+#if USE_EVP
+ if (evp_md_ctx != NULL) {
+ EVP_MD_CTX_destroy(evp_md_ctx);
+ dctx->ctxdata.evp_md_ctx = NULL;
+ }
+#else
+ switch (dctx->key->key_alg) {
+#ifndef PK11_MD5_DISABLE
+ case 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;
+ }
+ }
+ break;
+#endif
+ case DST_ALG_RSASHA1:
+ case DST_ALG_NSEC3RSASHA1:
+ {
+ 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;
+ }
+ }
+ break;
+ case DST_ALG_RSASHA256:
+ {
+ isc_sha256_t *sha256ctx = dctx->ctxdata.sha256ctx;
+
+ if (sha256ctx != NULL) {
+ isc_sha256_invalidate(sha256ctx);
+ isc_mem_put(dctx->mctx, sha256ctx,
+ sizeof(isc_sha256_t));
+ dctx->ctxdata.sha256ctx = NULL;
+ }
+ }
+ break;
+ case DST_ALG_RSASHA512:
+ {
+ isc_sha512_t *sha512ctx = dctx->ctxdata.sha512ctx;
+
+ if (sha512ctx != NULL) {
+ isc_sha512_invalidate(sha512ctx);
+ isc_mem_put(dctx->mctx, sha512ctx,
+ sizeof(isc_sha512_t));
+ dctx->ctxdata.sha512ctx = NULL;
+ }
+ }
+ break;
+ default:
+ INSIST(0);
+ }
+#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
+
+#ifndef PK11_MD5_DISABLE
+ REQUIRE(dctx->key->key_alg == DST_ALG_RSAMD5 ||
+ dctx->key->key_alg == DST_ALG_RSASHA1 ||
+ dctx->key->key_alg == DST_ALG_NSEC3RSASHA1 ||
+ dctx->key->key_alg == DST_ALG_RSASHA256 ||
+ dctx->key->key_alg == DST_ALG_RSASHA512);
+#else
+ REQUIRE(dctx->key->key_alg == DST_ALG_RSASHA1 ||
+ dctx->key->key_alg == DST_ALG_NSEC3RSASHA1 ||
+ dctx->key->key_alg == DST_ALG_RSASHA256 ||
+ dctx->key->key_alg == DST_ALG_RSASHA512);
+#endif
+
+#if USE_EVP
+ if (!EVP_DigestUpdate(evp_md_ctx, data->base, data->length)) {
+ return (dst__openssl_toresult3(dctx->category,
+ "EVP_DigestUpdate",
+ ISC_R_FAILURE));
+ }
+#else
+ switch (dctx->key->key_alg) {
+#ifndef PK11_MD5_DISABLE
+ case DST_ALG_RSAMD5:
+ {
+ isc_md5_t *md5ctx = dctx->ctxdata.md5ctx;
+
+ isc_md5_update(md5ctx, data->base, data->length);
+ }
+ break;
+#endif
+ case DST_ALG_RSASHA1:
+ case DST_ALG_NSEC3RSASHA1:
+ {
+ isc_sha1_t *sha1ctx = dctx->ctxdata.sha1ctx;
+
+ isc_sha1_update(sha1ctx, data->base, data->length);
+ }
+ break;
+ case DST_ALG_RSASHA256:
+ {
+ isc_sha256_t *sha256ctx = dctx->ctxdata.sha256ctx;
+
+ isc_sha256_update(sha256ctx, data->base, data->length);
+ }
+ break;
+ case DST_ALG_RSASHA512:
+ {
+ isc_sha512_t *sha512ctx = dctx->ctxdata.sha512ctx;
+
+ isc_sha512_update(sha512ctx, data->base, data->length);
+ }
+ break;
+ default:
+ INSIST(0);
+ }
+#endif
+ return (ISC_R_SUCCESS);
+}
+
+#if ! USE_EVP && OPENSSL_VERSION_NUMBER < 0x00908000L
+/*
+ * Digest prefixes from RFC 5702.
+ */
+static unsigned char sha256_prefix[] =
+ { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48,
+ 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20};
+static unsigned char sha512_prefix[] =
+ { 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48,
+ 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40};
+#define PREFIXLEN sizeof(sha512_prefix)
+#else
+#define PREFIXLEN 0
+#endif
+
+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_SHA512_DIGESTLENGTH >= ISC_*_DIGESTLENGTH */
+ unsigned char digest[PREFIXLEN + ISC_SHA512_DIGESTLENGTH];
+ int status;
+ int type = 0;
+ unsigned int digestlen = 0;
+#if OPENSSL_VERSION_NUMBER < 0x00908000L
+ unsigned int prefixlen = 0;
+ const unsigned char *prefix = NULL;
+#endif
+#endif
+
+#ifndef PK11_MD5_DISABLE
+ REQUIRE(dctx->key->key_alg == DST_ALG_RSAMD5 ||
+ dctx->key->key_alg == DST_ALG_RSASHA1 ||
+ dctx->key->key_alg == DST_ALG_NSEC3RSASHA1 ||
+ dctx->key->key_alg == DST_ALG_RSASHA256 ||
+ dctx->key->key_alg == DST_ALG_RSASHA512);
+#else
+ REQUIRE(dctx->key->key_alg == DST_ALG_RSASHA1 ||
+ dctx->key->key_alg == DST_ALG_NSEC3RSASHA1 ||
+ dctx->key->key_alg == DST_ALG_RSASHA256 ||
+ dctx->key->key_alg == DST_ALG_RSASHA512);
+#endif
+
+ 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 (dst__openssl_toresult3(dctx->category,
+ "EVP_SignFinal",
+ ISC_R_FAILURE));
+ }
+#else
+ if (r.length < (unsigned int) RSA_size(rsa))
+ return (ISC_R_NOSPACE);
+
+ switch (dctx->key->key_alg) {
+#ifndef PK11_MD5_DISABLE
+ case DST_ALG_RSAMD5:
+ {
+ isc_md5_t *md5ctx = dctx->ctxdata.md5ctx;
+
+ isc_md5_final(md5ctx, digest);
+ type = NID_md5;
+ digestlen = ISC_MD5_DIGESTLENGTH;
+ }
+ break;
+#endif
+ case DST_ALG_RSASHA1:
+ case DST_ALG_NSEC3RSASHA1:
+ {
+ isc_sha1_t *sha1ctx = dctx->ctxdata.sha1ctx;
+
+ isc_sha1_final(sha1ctx, digest);
+ type = NID_sha1;
+ digestlen = ISC_SHA1_DIGESTLENGTH;
+ }
+ break;
+ case DST_ALG_RSASHA256:
+ {
+ isc_sha256_t *sha256ctx = dctx->ctxdata.sha256ctx;
+
+ isc_sha256_final(digest, sha256ctx);
+ digestlen = ISC_SHA256_DIGESTLENGTH;
+#if OPENSSL_VERSION_NUMBER < 0x00908000L
+ prefix = sha256_prefix;
+ prefixlen = sizeof(sha256_prefix);
+#else
+ type = NID_sha256;
+#endif
+ }
+ break;
+ case DST_ALG_RSASHA512:
+ {
+ isc_sha512_t *sha512ctx = dctx->ctxdata.sha512ctx;
+
+ isc_sha512_final(digest, sha512ctx);
+ digestlen = ISC_SHA512_DIGESTLENGTH;
+#if OPENSSL_VERSION_NUMBER < 0x00908000L
+ prefix = sha512_prefix;
+ prefixlen = sizeof(sha512_prefix);
+#else
+ type = NID_sha512;
+#endif
+ }
+ break;
+ default:
+ INSIST(0);
+ }
+
+#if OPENSSL_VERSION_NUMBER < 0x00908000L
+ switch (dctx->key->key_alg) {
+#ifndef PK11_MD5_DISABLE
+ case DST_ALG_RSAMD5:
+#endif
+ case DST_ALG_RSASHA1:
+ case DST_ALG_NSEC3RSASHA1:
+ INSIST(type != 0);
+ status = RSA_sign(type, digest, digestlen, r.base,
+ &siglen, rsa);
+ break;
+
+ case DST_ALG_RSASHA256:
+ case DST_ALG_RSASHA512:
+ INSIST(prefix != NULL);
+ INSIST(prefixlen != 0);
+ INSIST(prefixlen + digestlen <= sizeof(digest));
+
+ memmove(digest + prefixlen, digest, digestlen);
+ memmove(digest, prefix, prefixlen);
+ status = RSA_private_encrypt(digestlen + prefixlen,
+ digest, r.base, rsa,
+ RSA_PKCS1_PADDING);
+ if (status < 0)
+ status = 0;
+ else
+ siglen = status;
+ break;
+
+ default:
+ INSIST(0);
+ }
+#else
+ INSIST(type != 0);
+ status = RSA_sign(type, digest, digestlen, r.base, &siglen, rsa);
+#endif
+ if (status == 0)
+ return (dst__openssl_toresult3(dctx->category,
+ "RSA_sign",
+ DST_R_OPENSSLFAILURE));
+#endif
+
+ isc_buffer_add(sig, siglen);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+opensslrsa_verify2(dst_context_t *dctx, int maxbits, const isc_region_t *sig) {
+ dst_key_t *key = dctx->key;
+ int status = 0;
+ const BIGNUM *e = NULL;
+#if USE_EVP
+ EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
+ EVP_PKEY *pkey = key->keydata.pkey;
+ RSA *rsa;
+ int bits;
+#else
+ /* note: ISC_SHA512_DIGESTLENGTH >= ISC_*_DIGESTLENGTH */
+ unsigned char digest[ISC_SHA512_DIGESTLENGTH];
+ int type = 0;
+ unsigned int digestlen = 0;
+ RSA *rsa = key->keydata.rsa;
+#if OPENSSL_VERSION_NUMBER < 0x00908000L
+ unsigned int prefixlen = 0;
+ const unsigned char *prefix = NULL;
+#endif
+#endif
+
+#ifndef PK11_MD5_DISABLE
+ REQUIRE(dctx->key->key_alg == DST_ALG_RSAMD5 ||
+ dctx->key->key_alg == DST_ALG_RSASHA1 ||
+ dctx->key->key_alg == DST_ALG_NSEC3RSASHA1 ||
+ dctx->key->key_alg == DST_ALG_RSASHA256 ||
+ dctx->key->key_alg == DST_ALG_RSASHA512);
+#else
+ REQUIRE(dctx->key->key_alg == DST_ALG_RSASHA1 ||
+ dctx->key->key_alg == DST_ALG_NSEC3RSASHA1 ||
+ dctx->key->key_alg == DST_ALG_RSASHA256 ||
+ dctx->key->key_alg == DST_ALG_RSASHA512);
+#endif
+
+#if USE_EVP
+ rsa = EVP_PKEY_get1_RSA(pkey);
+ if (rsa == NULL)
+ return (dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+ RSA_get0_key(rsa, NULL, &e, NULL);
+ bits = BN_num_bits(e);
+ RSA_free(rsa);
+ if (bits > maxbits && maxbits != 0)
+ return (DST_R_VERIFYFAILURE);
+
+ status = EVP_VerifyFinal(evp_md_ctx, sig->base, sig->length, pkey);
+ switch (status) {
+ case 1:
+ return (ISC_R_SUCCESS);
+ case 0:
+ return (dst__openssl_toresult(DST_R_VERIFYFAILURE));
+ default:
+ return (dst__openssl_toresult3(dctx->category,
+ "EVP_VerifyFinal",
+ DST_R_VERIFYFAILURE));
+ }
+#else
+ RSA_get0_key(rsa, NULL, &e, NULL);
+ if (BN_num_bits(e) > maxbits && maxbits != 0)
+ return (DST_R_VERIFYFAILURE);
+
+ switch (dctx->key->key_alg) {
+#ifndef PK11_MD5_DISABLE
+ case DST_ALG_RSAMD5:
+ {
+ isc_md5_t *md5ctx = dctx->ctxdata.md5ctx;
+
+ isc_md5_final(md5ctx, digest);
+ type = NID_md5;
+ digestlen = ISC_MD5_DIGESTLENGTH;
+ }
+ break;
+#endif
+ case DST_ALG_RSASHA1:
+ case DST_ALG_NSEC3RSASHA1:
+ {
+ isc_sha1_t *sha1ctx = dctx->ctxdata.sha1ctx;
+
+ isc_sha1_final(sha1ctx, digest);
+ type = NID_sha1;
+ digestlen = ISC_SHA1_DIGESTLENGTH;
+ }
+ break;
+ case DST_ALG_RSASHA256:
+ {
+ isc_sha256_t *sha256ctx = dctx->ctxdata.sha256ctx;
+
+ isc_sha256_final(digest, sha256ctx);
+ digestlen = ISC_SHA256_DIGESTLENGTH;
+#if OPENSSL_VERSION_NUMBER < 0x00908000L
+ prefix = sha256_prefix;
+ prefixlen = sizeof(sha256_prefix);
+#else
+ type = NID_sha256;
+#endif
+ }
+ break;
+ case DST_ALG_RSASHA512:
+ {
+ isc_sha512_t *sha512ctx = dctx->ctxdata.sha512ctx;
+
+ isc_sha512_final(digest, sha512ctx);
+ digestlen = ISC_SHA512_DIGESTLENGTH;
+#if OPENSSL_VERSION_NUMBER < 0x00908000L
+ prefix = sha512_prefix;
+ prefixlen = sizeof(sha512_prefix);
+#else
+ type = NID_sha512;
+#endif
+ }
+ break;
+ default:
+ INSIST(0);
+ }
+
+ if (sig->length != (unsigned int) RSA_size(rsa))
+ return (DST_R_VERIFYFAILURE);
+
+#if OPENSSL_VERSION_NUMBER < 0x00908000L
+ switch (dctx->key->key_alg) {
+#ifndef PK11_MD5_DISABLE
+ case DST_ALG_RSAMD5:
+#endif
+ case DST_ALG_RSASHA1:
+ case DST_ALG_NSEC3RSASHA1:
+ INSIST(type != 0);
+ status = RSA_verify(type, digest, digestlen, sig->base,
+ RSA_size(rsa), rsa);
+ break;
+
+ case DST_ALG_RSASHA256:
+ case DST_ALG_RSASHA512:
+ {
+ /*
+ * 1024 is big enough for all valid RSA bit sizes
+ * for use with DNSSEC.
+ */
+ unsigned char original[PREFIXLEN + 1024];
+
+ INSIST(prefix != NULL);
+ INSIST(prefixlen != 0U);
+
+ if (RSA_size(rsa) > (int)sizeof(original))
+ return (DST_R_VERIFYFAILURE);
+
+ status = RSA_public_decrypt(sig->length, sig->base,
+ original, rsa,
+ RSA_PKCS1_PADDING);
+ if (status <= 0)
+ return (dst__openssl_toresult3(
+ dctx->category,
+ "RSA_public_decrypt",
+ DST_R_VERIFYFAILURE));
+ if (status != (int)(prefixlen + digestlen))
+ return (DST_R_VERIFYFAILURE);
+ if (!isc_safe_memequal(original, prefix, prefixlen))
+ return (DST_R_VERIFYFAILURE);
+ if (!isc_safe_memequal(original + prefixlen,
+ digest, digestlen))
+ return (DST_R_VERIFYFAILURE);
+ status = 1;
+ }
+ break;
+
+ default:
+ INSIST(0);
+ }
+#else
+ INSIST(type != 0);
+ 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);
+#endif
+}
+
+static isc_result_t
+opensslrsa_verify(dst_context_t *dctx, const isc_region_t *sig) {
+ return (opensslrsa_verify2(dctx, 0, sig));
+}
+
+static bool
+opensslrsa_compare(const dst_key_t *key1, const dst_key_t *key2) {
+ int status;
+ RSA *rsa1 = NULL, *rsa2 = NULL;
+ const BIGNUM *n1 = NULL, *n2 = NULL;
+ const BIGNUM *e1 = NULL, *e2 = NULL;
+ const BIGNUM *d1 = NULL, *d2 = NULL;
+ const BIGNUM *p1 = NULL, *p2 = NULL;
+ const BIGNUM *q1 = NULL, *q2 = 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 (true);
+ else if (rsa1 == NULL || rsa2 == NULL)
+ return (false);
+
+ RSA_get0_key(rsa1, &n1, &e1, &d1);
+ RSA_get0_key(rsa2, &n2, &e2, &d2);
+ status = BN_cmp(n1, n2) || BN_cmp(e1, e2);
+
+ if (status != 0)
+ return (false);
+
+#if USE_EVP
+ if (RSA_test_flags(rsa1, RSA_FLAG_EXT_PKEY) != 0 ||
+ RSA_test_flags(rsa2, RSA_FLAG_EXT_PKEY) != 0) {
+ if (RSA_test_flags(rsa1, RSA_FLAG_EXT_PKEY) == 0 ||
+ RSA_test_flags(rsa2, RSA_FLAG_EXT_PKEY) == 0)
+ return (false);
+ /*
+ * Can't compare private parameters, BTW does it make sense?
+ */
+ return (true);
+ }
+#endif
+
+ if (d1 != NULL || d2 != NULL) {
+ if (d1 == NULL || d2 == NULL)
+ return (false);
+ RSA_get0_factors(rsa1, &p1, &q1);
+ RSA_get0_factors(rsa2, &p2, &q2);
+ status = BN_cmp(d1, d2) || BN_cmp(p1, p1) || BN_cmp(q1, q2);
+
+ if (status != 0)
+ return (false);
+ }
+ return (true);
+}
+
+#if OPENSSL_VERSION_NUMBER > 0x00908000L
+static int
+progress_cb(int p, int n, BN_GENCB *cb) {
+ union {
+ void *dptr;
+ void (*fptr)(int);
+ } u;
+
+ UNUSED(n);
+
+ u.dptr = BN_GENCB_get_arg(cb);
+ if (u.fptr != NULL)
+ u.fptr(p);
+ return (1);
+}
+#endif
+
+static isc_result_t
+opensslrsa_generate(dst_key_t *key, int exp, void (*callback)(int)) {
+#if OPENSSL_VERSION_NUMBER > 0x00908000L
+ isc_result_t ret = DST_R_OPENSSLFAILURE;
+ union {
+ void *dptr;
+ void (*fptr)(int);
+ } u;
+ RSA *rsa = RSA_new();
+ BIGNUM *e = BN_new();
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+ BN_GENCB _cb;
+#endif
+ BN_GENCB *cb = BN_GENCB_new();
+#if USE_EVP
+ EVP_PKEY *pkey = EVP_PKEY_new();
+#endif
+
+ /*
+ * Reject incorrect RSA key lengths.
+ */
+ switch (key->key_alg) {
+ case DST_ALG_RSAMD5:
+ case DST_ALG_RSASHA1:
+ case DST_ALG_NSEC3RSASHA1:
+ /* From RFC 3110 */
+ if (key->key_size > 4096)
+ goto err;
+ break;
+ case DST_ALG_RSASHA256:
+ /* From RFC 5702 */
+ if ((key->key_size < 512) ||
+ (key->key_size > 4096))
+ goto err;
+ break;
+ case DST_ALG_RSASHA512:
+ /* From RFC 5702 */
+ if ((key->key_size < 1024) ||
+ (key->key_size > 4096))
+ goto err;
+ break;
+ default:
+ INSIST(0);
+ }
+
+ if (rsa == NULL || e == NULL || cb == 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 {
+ /* (phased-out) F5 0x100000001 */
+ BN_set_bit(e, 0);
+ BN_set_bit(e, 32);
+ }
+
+ if (callback == NULL) {
+ BN_GENCB_set_old(cb, NULL, NULL);
+ } else {
+ u.fptr = callback;
+ BN_GENCB_set(cb, &progress_cb, u.dptr);
+ }
+
+ if (RSA_generate_key_ex(rsa, key->key_size, e, cb)) {
+ BN_free(e);
+ BN_GENCB_free(cb);
+ cb = NULL;
+ SET_FLAGS(rsa);
+#if USE_EVP
+ key->keydata.pkey = pkey;
+
+ RSA_free(rsa);
+#else
+ key->keydata.rsa = rsa;
+#endif
+ return (ISC_R_SUCCESS);
+ }
+ ret = dst__openssl_toresult2("RSA_generate_key_ex",
+ DST_R_OPENSSLFAILURE);
+
+ err:
+#if USE_EVP
+ if (pkey != NULL) {
+ EVP_PKEY_free(pkey);
+ pkey = NULL;
+ }
+#endif
+ if (e != NULL) {
+ BN_free(e);
+ e = NULL;
+ }
+ if (rsa != NULL) {
+ RSA_free(rsa);
+ rsa = NULL;
+ }
+ if (cb != NULL) {
+ BN_GENCB_free(cb);
+ cb = NULL;
+ }
+ return (dst__openssl_toresult(ret));
+#else
+ RSA *rsa;
+ unsigned long e;
+#if USE_EVP
+ EVP_PKEY *pkey = EVP_PKEY_new();
+
+ UNUSED(callback);
+
+ if (pkey == NULL)
+ return (ISC_R_NOMEMORY);
+#else
+ UNUSED(callback);
+#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_toresult2("RSA_generate_key",
+ 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 bool
+opensslrsa_isprivate(const dst_key_t *key) {
+ const BIGNUM *d = NULL;
+#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_test_flags(rsa, RSA_FLAG_EXT_PKEY) != 0)
+ return (true);
+ RSA_get0_key(rsa, NULL, NULL, &d);
+ return (rsa != NULL && 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
+ const BIGNUM *e = NULL, *n = NULL;
+
+#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);
+
+ RSA_get0_key(rsa, &n, &e, NULL);
+ mod_bytes = BN_num_bytes(n);
+ e_bytes = BN_num_bytes(e);
+
+ if (e_bytes < 256) { /*%< key exponent is <= 2040 bits */
+ if (r.length < 1)
+ DST_RET(ISC_R_NOSPACE);
+ isc_buffer_putuint8(data, (uint8_t) e_bytes);
+ isc_region_consume(&r, 1);
+ } else {
+ if (r.length < 3)
+ DST_RET(ISC_R_NOSPACE);
+ isc_buffer_putuint8(data, 0);
+ isc_buffer_putuint16(data, (uint16_t) e_bytes);
+ isc_region_consume(&r, 3);
+ }
+
+ if (r.length < e_bytes + mod_bytes)
+ DST_RET(ISC_R_NOSPACE);
+
+ RSA_get0_key(rsa, &n, &e, NULL);
+ BN_bn2bin(e, r.base);
+ isc_region_consume(&r, e_bytes);
+ BN_bn2bin(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;
+ unsigned int length;
+#if USE_EVP
+ EVP_PKEY *pkey;
+#endif
+ BIGNUM *e = NULL, *n = NULL;
+
+ isc_buffer_remainingregion(data, &r);
+ if (r.length == 0)
+ return (ISC_R_SUCCESS);
+ length = r.length;
+
+ 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;
+ isc_region_consume(&r, 1);
+
+ if (e_bytes == 0) {
+ if (r.length < 2) {
+ RSA_free(rsa);
+ return (DST_R_INVALIDPUBLICKEY);
+ }
+ e_bytes = (*r.base) << 8;
+ isc_region_consume(&r, 1);
+ e_bytes += *r.base;
+ isc_region_consume(&r, 1);
+ }
+
+ if (r.length < e_bytes) {
+ RSA_free(rsa);
+ return (DST_R_INVALIDPUBLICKEY);
+ }
+ e = BN_bin2bn(r.base, e_bytes, NULL);
+ isc_region_consume(&r, e_bytes);
+ n = BN_bin2bn(r.base, r.length, NULL);
+ if (RSA_set0_key(rsa, n, e, NULL) == 0) {
+ if (n != NULL) BN_free(n);
+ if (e != NULL) BN_free(e);
+ RSA_free(rsa);
+ return (ISC_R_NOMEMORY);
+ }
+ key->key_size = BN_num_bits(n);
+
+ isc_buffer_forward(data, 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;
+ const BIGNUM *n = NULL, *e = NULL, *d = NULL;
+ const BIGNUM *p = NULL, *q = NULL;
+ const BIGNUM *dmp1 = NULL, *dmq1 = NULL, *iqmp = NULL;
+
+#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
+ memset(bufs, 0, sizeof(bufs));
+
+ RSA_get0_key(rsa, &n, &e, &d);
+ RSA_get0_factors(rsa, &p, &q);
+ RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp);
+
+ if (key->external) {
+ priv.nelements = 0;
+ result = dst__privstruct_writefile(key, &priv, directory);
+ goto fail;
+ }
+
+ for (i = 0; i < 8; i++) {
+ bufs[i] = isc_mem_get(key->mctx, BN_num_bytes(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(n);
+ BN_bn2bin(n, bufs[i]);
+ priv.elements[i].data = bufs[i];
+ i++;
+
+ priv.elements[i].tag = TAG_RSA_PUBLICEXPONENT;
+ priv.elements[i].length = BN_num_bytes(e);
+ BN_bn2bin(e, bufs[i]);
+ priv.elements[i].data = bufs[i];
+ i++;
+
+ if (d != NULL) {
+ priv.elements[i].tag = TAG_RSA_PRIVATEEXPONENT;
+ priv.elements[i].length = BN_num_bytes(d);
+ BN_bn2bin(d, bufs[i]);
+ priv.elements[i].data = bufs[i];
+ i++;
+ }
+
+ if (p != NULL) {
+ priv.elements[i].tag = TAG_RSA_PRIME1;
+ priv.elements[i].length = BN_num_bytes(p);
+ BN_bn2bin(p, bufs[i]);
+ priv.elements[i].data = bufs[i];
+ i++;
+ }
+
+ if (q != NULL) {
+ priv.elements[i].tag = TAG_RSA_PRIME2;
+ priv.elements[i].length = BN_num_bytes(q);
+ BN_bn2bin(q, bufs[i]);
+ priv.elements[i].data = bufs[i];
+ i++;
+ }
+
+ if (dmp1 != NULL) {
+ priv.elements[i].tag = TAG_RSA_EXPONENT1;
+ priv.elements[i].length = BN_num_bytes(dmp1);
+ BN_bn2bin(dmp1, bufs[i]);
+ priv.elements[i].data = bufs[i];
+ i++;
+ }
+
+ if (dmq1 != NULL) {
+ priv.elements[i].tag = TAG_RSA_EXPONENT2;
+ priv.elements[i].length = BN_num_bytes(dmq1);
+ BN_bn2bin(dmq1, bufs[i]);
+ priv.elements[i].data = bufs[i];
+ i++;
+ }
+
+ if (iqmp != NULL) {
+ priv.elements[i].tag = TAG_RSA_COEFFICIENT;
+ priv.elements[i].length = BN_num_bytes(iqmp);
+ BN_bn2bin(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 =
+ (unsigned short)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 =
+ (unsigned short)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(n));
+ }
+ return (result);
+}
+
+static isc_result_t
+rsa_check(RSA *rsa, RSA *pub) {
+ const BIGNUM *n1 = NULL, *n2 = NULL;
+ const BIGNUM *e1 = NULL, *e2 = NULL;
+ BIGNUM *n = NULL, *e = NULL;
+
+ /*
+ * Public parameters should be the same but if they are not set
+ * copy them from the public key.
+ */
+ RSA_get0_key(rsa, &n1, &e1, NULL);
+ if (pub != NULL) {
+ RSA_get0_key(pub, &n2, &e2, NULL);
+ if (n1 != NULL) {
+ if (BN_cmp(n1, n2) != 0)
+ return (DST_R_INVALIDPRIVATEKEY);
+ } else {
+ n = BN_dup(n2);
+ }
+ if (e1 != NULL) {
+ if (BN_cmp(e1, e2) != 0)
+ return (DST_R_INVALIDPRIVATEKEY);
+ } else {
+ e = BN_dup(e2);
+ }
+ if (RSA_set0_key(rsa, n, e, NULL) == 0) {
+ if (n != NULL)
+ BN_free(n);
+ if (e != NULL)
+ BN_free(e);
+ }
+ }
+ RSA_get0_key(rsa, &n1, &e1, NULL);
+ if (n1 == NULL || e1 == NULL)
+ return (DST_R_INVALIDPRIVATEKEY);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+opensslrsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
+ dst_private_t priv;
+ isc_result_t ret;
+ int i;
+ RSA *rsa = NULL, *pubrsa = NULL;
+#if !defined(OPENSSL_NO_ENGINE)
+ ENGINE *ep = NULL;
+ const BIGNUM *ex = NULL;
+#endif
+ isc_mem_t *mctx = key->mctx;
+ const char *engine = NULL, *label = NULL;
+#if !defined(OPENSSL_NO_ENGINE) || USE_EVP
+ EVP_PKEY *pkey = NULL;
+#endif
+ BIGNUM *n = NULL, *e = NULL, *d = NULL;
+ BIGNUM *p = NULL, *q = NULL;
+ BIGNUM *dmp1 = NULL, *dmq1 = NULL, *iqmp = NULL;
+
+ /* read private key file */
+ ret = dst__privstruct_parse(key, DST_ALG_RSA, lexer, mctx, &priv);
+ if (ret != ISC_R_SUCCESS)
+ goto err;
+
+ if (key->external) {
+ if (priv.nelements != 0)
+ DST_RET(DST_R_INVALIDPRIVATEKEY);
+ if (pub == NULL)
+ DST_RET(DST_R_INVALIDPRIVATEKEY);
+ key->keydata.pkey = pub->keydata.pkey;
+ pub->keydata.pkey = NULL;
+ key->key_size = pub->key_size;
+ dst__privstruct_free(&priv, mctx);
+ isc_safe_memwipe(&priv, sizeof(priv));
+ return (ISC_R_SUCCESS);
+ }
+
+#if USE_EVP
+ if (pub != NULL && pub->keydata.pkey != NULL)
+ pubrsa = EVP_PKEY_get1_RSA(pub->keydata.pkey);
+#else
+ if (pub != NULL && pub->keydata.rsa != NULL) {
+ pubrsa = pub->keydata.rsa;
+ pub->keydata.rsa = NULL;
+ }
+#endif
+
+ for (i = 0; i < priv.nelements; i++) {
+ switch (priv.elements[i].tag) {
+ case TAG_RSA_ENGINE:
+ engine = (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 (label != NULL) {
+#if !defined(OPENSSL_NO_ENGINE)
+ if (engine == NULL)
+ DST_RET(DST_R_NOENGINE);
+ ep = dst__openssl_getengine(engine);
+ if (ep == NULL)
+ DST_RET(DST_R_NOENGINE);
+ pkey = ENGINE_load_private_key(ep, label, NULL, NULL);
+ if (pkey == NULL)
+ DST_RET(dst__openssl_toresult2(
+ "ENGINE_load_private_key",
+ ISC_R_NOTFOUND));
+ key->engine = isc_mem_strdup(key->mctx, engine);
+ 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);
+ rsa = EVP_PKEY_get1_RSA(pkey);
+ if (rsa == NULL)
+ DST_RET(dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+ if (rsa_check(rsa, pubrsa) != ISC_R_SUCCESS)
+ DST_RET(DST_R_INVALIDPRIVATEKEY);
+ RSA_get0_key(rsa, NULL, &ex, NULL);
+ if (BN_num_bits(ex) > RSA_MAX_PUBEXP_BITS)
+ DST_RET(ISC_R_RANGE);
+ if (pubrsa != NULL)
+ RSA_free(pubrsa);
+ key->key_size = EVP_PKEY_bits(pkey);
+#if USE_EVP
+ key->keydata.pkey = pkey;
+ RSA_free(rsa);
+#else
+ key->keydata.rsa = rsa;
+ EVP_PKEY_free(pkey);
+#endif
+ dst__privstruct_free(&priv, mctx);
+ isc_safe_memwipe(&priv, sizeof(priv));
+ return (ISC_R_SUCCESS);
+#else
+ DST_RET(DST_R_NOENGINE);
+#endif
+ }
+
+ 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;
+ 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:
+ n = bn;
+ break;
+ case TAG_RSA_PUBLICEXPONENT:
+ e = bn;
+ break;
+ case TAG_RSA_PRIVATEEXPONENT:
+ d = bn;
+ break;
+ case TAG_RSA_PRIME1:
+ p = bn;
+ break;
+ case TAG_RSA_PRIME2:
+ q = bn;
+ break;
+ case TAG_RSA_EXPONENT1:
+ dmp1 = bn;
+ break;
+ case TAG_RSA_EXPONENT2:
+ dmq1 = bn;
+ break;
+ case TAG_RSA_COEFFICIENT:
+ iqmp = bn;
+ break;
+ }
+ }
+ }
+ dst__privstruct_free(&priv, mctx);
+ isc_safe_memwipe(&priv, sizeof(priv));
+
+ if (RSA_set0_key(rsa, n, e, d) == 0) {
+ if (n != NULL) BN_free(n);
+ if (e != NULL) BN_free(e);
+ if (d != NULL) BN_free(d);
+ }
+ if (RSA_set0_factors(rsa, p, q) == 0) {
+ if (p != NULL) BN_free(p);
+ if (q != NULL) BN_free(q);
+ }
+ if (RSA_set0_crt_params(rsa, dmp1, dmq1, iqmp) == 0) {
+ if (dmp1 != NULL) BN_free(dmp1);
+ if (dmq1 != NULL) BN_free(dmq1);
+ if (iqmp != NULL) BN_free(iqmp);
+ }
+
+ if (rsa_check(rsa, pubrsa) != ISC_R_SUCCESS)
+ DST_RET(DST_R_INVALIDPRIVATEKEY);
+ if (BN_num_bits(e) > RSA_MAX_PUBEXP_BITS)
+ DST_RET(ISC_R_RANGE);
+ key->key_size = BN_num_bits(n);
+ if (pubrsa != NULL)
+ RSA_free(pubrsa);
+#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);
+ if (pubrsa != NULL)
+ RSA_free(pubrsa);
+ key->keydata.generic = NULL;
+ dst__privstruct_free(&priv, mctx);
+ isc_safe_memwipe(&priv, sizeof(priv));
+ return (ret);
+}
+
+static isc_result_t
+opensslrsa_fromlabel(dst_key_t *key, const char *engine, const char *label,
+ const char *pin)
+{
+#if !defined(OPENSSL_NO_ENGINE)
+ ENGINE *e = NULL;
+ isc_result_t ret;
+ EVP_PKEY *pkey = NULL;
+ RSA *rsa = NULL, *pubrsa = NULL;
+ char *colon, *tmpengine = NULL;
+ const BIGNUM *ex = NULL;
+
+ UNUSED(pin);
+
+ if (engine == NULL) {
+ if (strchr(label, ':') == NULL)
+ DST_RET(DST_R_NOENGINE);
+ tmpengine = isc_mem_strdup(key->mctx, label);
+ if (tmpengine == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ colon = strchr(tmpengine, ':');
+ INSIST(colon != NULL);
+ *colon = '\0';
+ }
+ e = dst__openssl_getengine(engine);
+ if (e == NULL)
+ DST_RET(DST_R_NOENGINE);
+ pkey = ENGINE_load_public_key(e, label, NULL, NULL);
+ if (pkey != NULL) {
+ pubrsa = EVP_PKEY_get1_RSA(pkey);
+ EVP_PKEY_free(pkey);
+ if (pubrsa == NULL)
+ DST_RET(dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+ }
+ pkey = ENGINE_load_private_key(e, label, NULL, NULL);
+ if (pkey == NULL)
+ DST_RET(dst__openssl_toresult2("ENGINE_load_private_key",
+ ISC_R_NOTFOUND));
+ if (tmpengine != NULL) {
+ key->engine = tmpengine;
+ tmpengine = NULL;
+ } else {
+ key->engine = isc_mem_strdup(key->mctx, engine);
+ 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);
+ rsa = EVP_PKEY_get1_RSA(pkey);
+ if (rsa == NULL)
+ DST_RET(dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+ if (rsa_check(rsa, pubrsa) != ISC_R_SUCCESS)
+ DST_RET(DST_R_INVALIDPRIVATEKEY);
+ RSA_get0_key(rsa, NULL, &ex, NULL);
+ if (BN_num_bits(ex) > RSA_MAX_PUBEXP_BITS)
+ DST_RET(ISC_R_RANGE);
+ if (pubrsa != NULL)
+ RSA_free(pubrsa);
+ key->key_size = EVP_PKEY_bits(pkey);
+#if USE_EVP
+ key->keydata.pkey = pkey;
+ RSA_free(rsa);
+#else
+ key->keydata.rsa = rsa;
+ EVP_PKEY_free(pkey);
+#endif
+ return (ISC_R_SUCCESS);
+
+ err:
+ if (tmpengine != NULL)
+ isc_mem_free(key->mctx, tmpengine);
+ if (rsa != NULL)
+ RSA_free(rsa);
+ if (pubrsa != NULL)
+ RSA_free(pubrsa);
+ if (pkey != NULL)
+ EVP_PKEY_free(pkey);
+ return (ret);
+#else
+ UNUSED(key);
+ UNUSED(engine);
+ UNUSED(label);
+ UNUSED(pin);
+ return(DST_R_NOENGINE);
+#endif
+}
+
+static dst_func_t opensslrsa_functions = {
+ opensslrsa_createctx,
+ NULL, /*%< createctx2 */
+ opensslrsa_destroyctx,
+ opensslrsa_adddata,
+ opensslrsa_sign,
+ opensslrsa_verify,
+ opensslrsa_verify2,
+ NULL, /*%< computesecret */
+ opensslrsa_compare,
+ NULL, /*%< paramcompare */
+ opensslrsa_generate,
+ opensslrsa_isprivate,
+ opensslrsa_destroy,
+ opensslrsa_todns,
+ opensslrsa_fromdns,
+ opensslrsa_tofile,
+ opensslrsa_parse,
+ NULL, /*%< cleanup */
+ opensslrsa_fromlabel,
+ NULL, /*%< dump */
+ NULL, /*%< restore */
+};
+
+isc_result_t
+dst__opensslrsa_init(dst_func_t **funcp, unsigned char algorithm) {
+ REQUIRE(funcp != NULL);
+
+ if (*funcp == NULL) {
+ switch (algorithm) {
+ case DST_ALG_RSASHA256:
+#if defined(HAVE_EVP_SHA256) || !USE_EVP
+ *funcp = &opensslrsa_functions;
+#endif
+ break;
+ case DST_ALG_RSASHA512:
+#if defined(HAVE_EVP_SHA512) || !USE_EVP
+ *funcp = &opensslrsa_functions;
+#endif
+ break;
+ default:
+ *funcp = &opensslrsa_functions;
+ break;
+ }
+ }
+ 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..6ffc4a0
--- /dev/null
+++ b/lib/dns/order.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $Id: order.c,v 1.10 2007/06/19 23:47:16 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdbool.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 bool
+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 (DNS_RDATASETATTR_RANDOMIZE);
+}
+
+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..3a781d2
--- /dev/null
+++ b/lib/dns/peer.c
@@ -0,0 +1,887 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $Id: peer.c,v 1.33 2009/09/02 23:48:02 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.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
+#define SEND_COOKIE_BIT 9
+#define NOTIFY_DSCP_BIT 10
+#define TRANSFER_DSCP_BIT 11
+#define QUERY_DSCP_BIT 12
+#define REQUEST_EXPIRE_BIT 13
+#define EDNS_VERSION_BIT 14
+#define FORCE_TCP_BIT 15
+
+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 = false;
+ peer->transfer_format = dns_one_answer;
+ peer->transfers = 0;
+ peer->request_ixfr = false;
+ peer->provide_ixfr = 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->query_source != NULL)
+ isc_mem_put(mem, p->query_source, sizeof(*p->query_source));
+
+ if (p->notify_source != NULL)
+ isc_mem_put(mem, p->notify_source, sizeof(*p->notify_source));
+
+ 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, bool newval) {
+ bool 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, bool *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, bool newval) {
+ bool 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, bool *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, bool newval) {
+ bool 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, bool *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, bool newval) {
+ bool 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, bool *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, bool newval) {
+ bool 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, bool *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_setsendcookie(dns_peer_t *peer, bool newval) {
+ bool existed;
+
+ REQUIRE(DNS_PEER_VALID(peer));
+
+ existed = DNS_BIT_CHECK(SEND_COOKIE_BIT, &peer->bitflags);
+
+ peer->send_cookie = newval;
+ DNS_BIT_SET(SEND_COOKIE_BIT, &peer->bitflags);
+
+ return (existed ? ISC_R_EXISTS : ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_peer_getsendcookie(dns_peer_t *peer, bool *retval) {
+ REQUIRE(DNS_PEER_VALID(peer));
+ REQUIRE(retval != NULL);
+
+ if (DNS_BIT_CHECK(SEND_COOKIE_BIT, &peer->bitflags)) {
+ *retval = peer->send_cookie;
+ return (ISC_R_SUCCESS);
+ } else
+ return (ISC_R_NOTFOUND);
+}
+
+isc_result_t
+dns_peer_setrequestexpire(dns_peer_t *peer, bool newval) {
+ bool existed;
+
+ REQUIRE(DNS_PEER_VALID(peer));
+
+ existed = DNS_BIT_CHECK(REQUEST_EXPIRE_BIT, &peer->bitflags);
+
+ peer->request_expire = newval;
+ DNS_BIT_SET(REQUEST_EXPIRE_BIT, &peer->bitflags);
+
+ return (existed ? ISC_R_EXISTS : ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_peer_getrequestexpire(dns_peer_t *peer, bool *retval) {
+ REQUIRE(DNS_PEER_VALID(peer));
+ REQUIRE(retval != NULL);
+
+ if (DNS_BIT_CHECK(REQUEST_EXPIRE_BIT, &peer->bitflags)) {
+ *retval = peer->request_expire;
+ return (ISC_R_SUCCESS);
+ } else
+ return (ISC_R_NOTFOUND);
+}
+
+isc_result_t
+dns_peer_setforcetcp(dns_peer_t *peer, bool newval) {
+ bool existed;
+
+ REQUIRE(DNS_PEER_VALID(peer));
+
+ existed = DNS_BIT_CHECK(FORCE_TCP_BIT, &peer->bitflags);
+
+ peer->force_tcp = newval;
+ DNS_BIT_SET(FORCE_TCP_BIT, &peer->bitflags);
+
+ return (existed ? ISC_R_EXISTS : ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_peer_getforcetcp(dns_peer_t *peer, bool *retval) {
+ REQUIRE(DNS_PEER_VALID(peer));
+ REQUIRE(retval != NULL);
+
+ if (DNS_BIT_CHECK(FORCE_TCP_BIT, &peer->bitflags)) {
+ *retval = peer->force_tcp;
+ return (ISC_R_SUCCESS);
+ } else
+ return (ISC_R_NOTFOUND);
+}
+
+isc_result_t
+dns_peer_settransfers(dns_peer_t *peer, uint32_t newval) {
+ bool 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, 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) {
+ bool 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) {
+ bool exists = false;
+
+ if (peer->key != NULL) {
+ dns_name_free(peer->key, peer->mem);
+ isc_mem_put(peer->mem, peer->key, sizeof(dns_name_t));
+ exists = 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_constinit(&b, keyval, strlen(keyval));
+ isc_buffer_add(&b, strlen(keyval));
+ result = dns_name_fromtext(dns_fixedname_name(&fname), &b,
+ dns_rootname, 0, 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, uint16_t udpsize) {
+ bool 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, 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, uint16_t maxudp) {
+ bool 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, 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);
+ }
+}
+
+isc_result_t
+dns_peer_setnotifydscp(dns_peer_t *peer, isc_dscp_t dscp) {
+ REQUIRE(DNS_PEER_VALID(peer));
+ REQUIRE(dscp < 64);
+
+ peer->notify_dscp = dscp;
+ DNS_BIT_SET(NOTIFY_DSCP_BIT, &peer->bitflags);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_peer_getnotifydscp(dns_peer_t *peer, isc_dscp_t *dscpp) {
+ REQUIRE(DNS_PEER_VALID(peer));
+ REQUIRE(dscpp != NULL);
+
+ if (DNS_BIT_CHECK(NOTIFY_DSCP_BIT, &peer->bitflags)) {
+ *dscpp = peer->notify_dscp;
+ return (ISC_R_SUCCESS);
+ }
+ return (ISC_R_NOTFOUND);
+}
+
+isc_result_t
+dns_peer_settransferdscp(dns_peer_t *peer, isc_dscp_t dscp) {
+ REQUIRE(DNS_PEER_VALID(peer));
+ REQUIRE(dscp < 64);
+
+ peer->transfer_dscp = dscp;
+ DNS_BIT_SET(TRANSFER_DSCP_BIT, &peer->bitflags);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_peer_gettransferdscp(dns_peer_t *peer, isc_dscp_t *dscpp) {
+ REQUIRE(DNS_PEER_VALID(peer));
+ REQUIRE(dscpp != NULL);
+
+ if (DNS_BIT_CHECK(TRANSFER_DSCP_BIT, &peer->bitflags)) {
+ *dscpp = peer->transfer_dscp;
+ return (ISC_R_SUCCESS);
+ }
+ return (ISC_R_NOTFOUND);
+}
+
+isc_result_t
+dns_peer_setquerydscp(dns_peer_t *peer, isc_dscp_t dscp) {
+ REQUIRE(DNS_PEER_VALID(peer));
+ REQUIRE(dscp < 64);
+
+ peer->query_dscp = dscp;
+ DNS_BIT_SET(QUERY_DSCP_BIT, &peer->bitflags);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_peer_getquerydscp(dns_peer_t *peer, isc_dscp_t *dscpp) {
+ REQUIRE(DNS_PEER_VALID(peer));
+ REQUIRE(dscpp != NULL);
+
+ if (DNS_BIT_CHECK(QUERY_DSCP_BIT, &peer->bitflags)) {
+ *dscpp = peer->query_dscp;
+ return (ISC_R_SUCCESS);
+ }
+ return (ISC_R_NOTFOUND);
+}
+
+isc_result_t
+dns_peer_setednsversion(dns_peer_t *peer, uint8_t ednsversion) {
+ REQUIRE(DNS_PEER_VALID(peer));
+
+ peer->ednsversion = ednsversion;
+ DNS_BIT_SET(EDNS_VERSION_BIT, &peer->bitflags);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_peer_getednsversion(dns_peer_t *peer, uint8_t *ednsversion) {
+ REQUIRE(DNS_PEER_VALID(peer));
+ REQUIRE(ednsversion != NULL);
+
+ if (DNS_BIT_CHECK(EDNS_VERSION_BIT, &peer->bitflags)) {
+ *ednsversion = peer->ednsversion;
+ return (ISC_R_SUCCESS);
+ } else
+ return (ISC_R_NOTFOUND);
+}
diff --git a/lib/dns/pkcs11.c b/lib/dns/pkcs11.c
new file mode 100644
index 0000000..5a2c502
--- /dev/null
+++ b/lib/dns/pkcs11.c
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifdef PKCS11CRYPTO
+
+#include <config.h>
+
+#include <dns/log.h>
+#include <dns/result.h>
+
+#include <pk11/pk11.h>
+#include <pk11/internal.h>
+
+#include "dst_pkcs11.h"
+
+isc_result_t
+dst__pkcs11_toresult(const char *funcname, const char *file, int line,
+ isc_result_t fallback, CK_RV rv)
+{
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_CRYPTO, ISC_LOG_WARNING,
+ "%s:%d: %s: Error = 0x%.8lX\n",
+ file, line, funcname, rv);
+ if (rv == CKR_HOST_MEMORY)
+ return (ISC_R_NOMEMORY);
+ return (fallback);
+}
+
+
+#else /* PKCS11CRYPTO */
+
+#include <isc/util.h>
+
+EMPTY_TRANSLATION_UNIT
+
+#endif /* PKCS11CRYPTO */
+/*! \file */
diff --git a/lib/dns/pkcs11dh_link.c b/lib/dns/pkcs11dh_link.c
new file mode 100644
index 0000000..e2b60ea
--- /dev/null
+++ b/lib/dns/pkcs11dh_link.c
@@ -0,0 +1,1136 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifdef PKCS11CRYPTO
+
+#include <config.h>
+
+#include <pk11/site.h>
+
+#ifndef PK11_DH_DISABLE
+
+#include <ctype.h>
+#include <stdbool.h>
+
+#include <isc/mem.h>
+#include <isc/safe.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dst/result.h>
+
+#include "dst_internal.h"
+#include "dst_parse.h"
+#include "dst_pkcs11.h"
+
+#include <pk11/pk11.h>
+#include <pk11/internal.h>
+#define WANT_DH_PRIMES
+#include <pk11/constants.h>
+
+#include <pkcs11/pkcs11.h>
+
+/*
+ * PKCS#3 DH keys:
+ * mechanisms:
+ * CKM_DH_PKCS_PARAMETER_GEN,
+ * CKM_DH_PKCS_KEY_PAIR_GEN,
+ * CKM_DH_PKCS_DERIVE
+ * domain parameters:
+ * object class CKO_DOMAIN_PARAMETERS
+ * key type CKK_DH
+ * attribute CKA_PRIME (prime p)
+ * attribute CKA_BASE (base g)
+ * optional attribute CKA_PRIME_BITS (p length in bits)
+ * public key:
+ * object class CKO_PUBLIC_KEY
+ * key type CKK_DH
+ * attribute CKA_PRIME (prime p)
+ * attribute CKA_BASE (base g)
+ * attribute CKA_VALUE (public value y)
+ * private key:
+ * object class CKO_PRIVATE_KEY
+ * key type CKK_DH
+ * attribute CKA_PRIME (prime p)
+ * attribute CKA_BASE (base g)
+ * attribute CKA_VALUE (private value x)
+ * optional attribute CKA_VALUE_BITS (x length in bits)
+ * reuse CKA_PRIVATE_EXPONENT for key pair private value
+ */
+
+#define CKA_VALUE2 CKA_PRIVATE_EXPONENT
+
+static CK_BBOOL truevalue = TRUE;
+static CK_BBOOL falsevalue = FALSE;
+
+#define DST_RET(a) {ret = a; goto err;}
+
+static void pkcs11dh_destroy(dst_key_t *key);
+static isc_result_t pkcs11dh_todns(const dst_key_t *key, isc_buffer_t *data);
+
+static isc_result_t
+pkcs11dh_loadpriv(const dst_key_t *key,
+ CK_SESSION_HANDLE session,
+ CK_OBJECT_HANDLE *hKey)
+{
+ CK_RV rv;
+ CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY;
+ CK_KEY_TYPE keyType = CKK_DH;
+ CK_ATTRIBUTE keyTemplate[] =
+ {
+ { CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+ { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+ { CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_SENSITIVE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_DERIVE, &truevalue, (CK_ULONG) sizeof(truevalue) },
+ { CKA_PRIME, NULL, 0 },
+ { CKA_BASE, NULL, 0 },
+ { CKA_VALUE, NULL, 0 }
+ };
+ CK_ATTRIBUTE *attr;
+ const pk11_object_t *priv;
+ isc_result_t ret;
+ unsigned int i;
+
+ priv = key->keydata.pkey;
+ if ((priv->object != CK_INVALID_HANDLE) && priv->ontoken) {
+ *hKey = priv->object;
+ return (ISC_R_SUCCESS);
+ }
+
+ attr = pk11_attribute_bytype(priv, CKA_PRIME);
+ if (attr == NULL)
+ return (DST_R_INVALIDPRIVATEKEY);
+ keyTemplate[6].pValue = isc_mem_get(key->mctx, attr->ulValueLen);
+ if (keyTemplate[6].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[6].pValue, attr->pValue, attr->ulValueLen);
+ keyTemplate[6].ulValueLen = attr->ulValueLen;
+
+ attr = pk11_attribute_bytype(priv, CKA_BASE);
+ if (attr == NULL)
+ DST_RET(DST_R_INVALIDPRIVATEKEY);
+ keyTemplate[7].pValue = isc_mem_get(key->mctx, attr->ulValueLen);
+ if (keyTemplate[7].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[7].pValue, attr->pValue, attr->ulValueLen);
+ keyTemplate[7].ulValueLen = attr->ulValueLen;
+
+ attr = pk11_attribute_bytype(priv, CKA_VALUE2);
+ if (attr == NULL)
+ DST_RET(DST_R_INVALIDPRIVATEKEY);
+ keyTemplate[8].pValue = isc_mem_get(key->mctx, attr->ulValueLen);
+ if (keyTemplate[8].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[8].pValue, attr->pValue, attr->ulValueLen);
+ keyTemplate[8].ulValueLen = attr->ulValueLen;
+
+ PK11_CALL(pkcs_C_CreateObject,
+ (session, keyTemplate, (CK_ULONG) 9, hKey),
+ DST_R_COMPUTESECRETFAILURE);
+ if (rv == CKR_OK)
+ ret = ISC_R_SUCCESS;
+
+ err:
+ for (i = 6; i <= 8; i++)
+ if (keyTemplate[i].pValue != NULL) {
+ isc_safe_memwipe(keyTemplate[i].pValue,
+ keyTemplate[i].ulValueLen);
+ isc_mem_put(key->mctx,
+ keyTemplate[i].pValue,
+ keyTemplate[i].ulValueLen);
+ }
+ return (ret);
+}
+
+static isc_result_t
+pkcs11dh_computesecret(const dst_key_t *pub, const dst_key_t *priv,
+ isc_buffer_t *secret)
+{
+ CK_RV rv;
+ CK_MECHANISM mech = { CKM_DH_PKCS_DERIVE, NULL, 0 };
+ CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY;
+ CK_KEY_TYPE keyType = CKK_GENERIC_SECRET;
+ CK_OBJECT_HANDLE hDerived = CK_INVALID_HANDLE;
+ CK_OBJECT_HANDLE hKey = CK_INVALID_HANDLE;
+ CK_ATTRIBUTE *attr;
+ CK_ULONG secLen;
+ CK_ATTRIBUTE keyTemplate[] =
+ {
+ { CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+ { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+ { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_SENSITIVE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_EXTRACTABLE, &truevalue, (CK_ULONG) sizeof(truevalue) },
+ { CKA_VALUE_LEN, &secLen, (CK_ULONG) sizeof(secLen) }
+ };
+ CK_ATTRIBUTE valTemplate[] =
+ {
+ { CKA_VALUE, NULL, 0 }
+ };
+ CK_BYTE *secValue;
+ pk11_context_t ctx;
+ isc_result_t ret;
+ unsigned int i;
+ isc_region_t r;
+
+ REQUIRE(pub->keydata.pkey != NULL);
+ REQUIRE(priv->keydata.pkey != NULL);
+ REQUIRE(priv->keydata.pkey->repr != NULL);
+ attr = pk11_attribute_bytype(pub->keydata.pkey, CKA_PRIME);
+ if (attr == NULL)
+ return (DST_R_INVALIDPUBLICKEY);
+ REQUIRE(attr != NULL);
+ secLen = attr->ulValueLen;
+ attr = pk11_attribute_bytype(pub->keydata.pkey, CKA_VALUE);
+ if (attr == NULL)
+ return (DST_R_INVALIDPUBLICKEY);
+
+ ret = pk11_get_session(&ctx, OP_DH, true, false,
+ priv->keydata.pkey->reqlogon, NULL,
+ pk11_get_best_token(OP_DH));
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+
+ mech.ulParameterLen = attr->ulValueLen;
+ mech.pParameter = isc_mem_get(pub->mctx, mech.ulParameterLen);
+ if (mech.pParameter == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(mech.pParameter, attr->pValue, mech.ulParameterLen);
+
+ ret = pkcs11dh_loadpriv(priv, ctx.session, &hKey);
+ if (ret != ISC_R_SUCCESS)
+ goto err;
+
+ PK11_RET(pkcs_C_DeriveKey,
+ (ctx.session, &mech, hKey,
+ keyTemplate, (CK_ULONG) 6, &hDerived),
+ DST_R_COMPUTESECRETFAILURE);
+
+ attr = valTemplate;
+ PK11_RET(pkcs_C_GetAttributeValue,
+ (ctx.session, hDerived, attr, (CK_ULONG) 1),
+ DST_R_CRYPTOFAILURE);
+ attr->pValue = isc_mem_get(pub->mctx, attr->ulValueLen);
+ if (attr->pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(attr->pValue, 0, attr->ulValueLen);
+ PK11_RET(pkcs_C_GetAttributeValue,
+ (ctx.session, hDerived, attr, (CK_ULONG) 1),
+ DST_R_CRYPTOFAILURE);
+
+ /* strip leading zeros */
+ secValue = (CK_BYTE_PTR) attr->pValue;
+ for (i = 0; i < attr->ulValueLen; i++)
+ if (secValue[i] != 0)
+ break;
+ isc_buffer_availableregion(secret, &r);
+ if (r.length < attr->ulValueLen - i)
+ DST_RET(ISC_R_NOSPACE);
+ memmove(r.base, secValue + i, attr->ulValueLen - i);
+ isc_buffer_add(secret, attr->ulValueLen - i);
+ ret = ISC_R_SUCCESS;
+
+ err:
+ if (hDerived != CK_INVALID_HANDLE)
+ (void) pkcs_C_DestroyObject(ctx.session, hDerived);
+ if (valTemplate[0].pValue != NULL) {
+ isc_safe_memwipe(valTemplate[0].pValue,
+ valTemplate[0].ulValueLen);
+ isc_mem_put(pub->mctx,
+ valTemplate[0].pValue,
+ valTemplate[0].ulValueLen);
+ }
+ if ((hKey != CK_INVALID_HANDLE) && !priv->keydata.pkey->ontoken)
+ (void) pkcs_C_DestroyObject(ctx.session, hKey);
+ if (mech.pParameter != NULL) {
+ isc_safe_memwipe(mech.pParameter, mech.ulParameterLen);
+ isc_mem_put(pub->mctx, mech.pParameter, mech.ulParameterLen);
+ }
+ pk11_return_session(&ctx);
+ return (ret);
+}
+
+static bool
+pkcs11dh_compare(const dst_key_t *key1, const dst_key_t *key2) {
+ pk11_object_t *dh1, *dh2;
+ CK_ATTRIBUTE *attr1, *attr2;
+
+ dh1 = key1->keydata.pkey;
+ dh2 = key2->keydata.pkey;
+
+ if ((dh1 == NULL) && (dh2 == NULL))
+ return (true);
+ else if ((dh1 == NULL) || (dh2 == NULL))
+ return (false);
+
+ attr1 = pk11_attribute_bytype(dh1, CKA_PRIME);
+ attr2 = pk11_attribute_bytype(dh2, CKA_PRIME);
+ if ((attr1 == NULL) && (attr2 == NULL))
+ return (true);
+ else if ((attr1 == NULL) || (attr2 == NULL) ||
+ (attr1->ulValueLen != attr2->ulValueLen) ||
+ !isc_safe_memequal(attr1->pValue, attr2->pValue,
+ attr1->ulValueLen))
+ return (false);
+
+ attr1 = pk11_attribute_bytype(dh1, CKA_BASE);
+ attr2 = pk11_attribute_bytype(dh2, CKA_BASE);
+ if ((attr1 == NULL) && (attr2 == NULL))
+ return (true);
+ else if ((attr1 == NULL) || (attr2 == NULL) ||
+ (attr1->ulValueLen != attr2->ulValueLen) ||
+ !isc_safe_memequal(attr1->pValue, attr2->pValue,
+ attr1->ulValueLen))
+ return (false);
+
+ attr1 = pk11_attribute_bytype(dh1, CKA_VALUE);
+ attr2 = pk11_attribute_bytype(dh2, CKA_VALUE);
+ if ((attr1 == NULL) && (attr2 == NULL))
+ return (true);
+ else if ((attr1 == NULL) || (attr2 == NULL) ||
+ (attr1->ulValueLen != attr2->ulValueLen) ||
+ !isc_safe_memequal(attr1->pValue, attr2->pValue,
+ attr1->ulValueLen))
+ return (false);
+
+ attr1 = pk11_attribute_bytype(dh1, CKA_VALUE2);
+ attr2 = pk11_attribute_bytype(dh2, CKA_VALUE2);
+ if (((attr1 != NULL) || (attr2 != NULL)) &&
+ ((attr1 == NULL) || (attr2 == NULL) ||
+ (attr1->ulValueLen != attr2->ulValueLen) ||
+ !isc_safe_memequal(attr1->pValue, attr2->pValue,
+ attr1->ulValueLen)))
+ return (false);
+
+ if (!dh1->ontoken && !dh2->ontoken)
+ return (true);
+ else if (dh1->ontoken || dh2->ontoken ||
+ (dh1->object != dh2->object))
+ return (false);
+
+ return (true);
+}
+
+static bool
+pkcs11dh_paramcompare(const dst_key_t *key1, const dst_key_t *key2) {
+ pk11_object_t *dh1, *dh2;
+ CK_ATTRIBUTE *attr1, *attr2;
+
+ dh1 = key1->keydata.pkey;
+ dh2 = key2->keydata.pkey;
+
+ if ((dh1 == NULL) && (dh2 == NULL))
+ return (true);
+ else if ((dh1 == NULL) || (dh2 == NULL))
+ return (false);
+
+ attr1 = pk11_attribute_bytype(dh1, CKA_PRIME);
+ attr2 = pk11_attribute_bytype(dh2, CKA_PRIME);
+ if ((attr1 == NULL) && (attr2 == NULL))
+ return (true);
+ else if ((attr1 == NULL) || (attr2 == NULL) ||
+ (attr1->ulValueLen != attr2->ulValueLen) ||
+ !isc_safe_memequal(attr1->pValue, attr2->pValue,
+ attr1->ulValueLen))
+ return (false);
+
+ attr1 = pk11_attribute_bytype(dh1, CKA_BASE);
+ attr2 = pk11_attribute_bytype(dh2, CKA_BASE);
+ if ((attr1 == NULL) && (attr2 == NULL))
+ return (true);
+ else if ((attr1 == NULL) || (attr2 == NULL) ||
+ (attr1->ulValueLen != attr2->ulValueLen) ||
+ !isc_safe_memequal(attr1->pValue, attr2->pValue,
+ attr1->ulValueLen))
+ return (false);
+
+ return (true);
+}
+
+static isc_result_t
+pkcs11dh_generate(dst_key_t *key, int generator, void (*callback)(int)) {
+ CK_RV rv;
+ CK_MECHANISM mech = { CKM_DH_PKCS_PARAMETER_GEN, NULL, 0 };
+ CK_OBJECT_HANDLE domainparams = CK_INVALID_HANDLE;
+ CK_OBJECT_CLASS dClass = CKO_DOMAIN_PARAMETERS;
+ CK_KEY_TYPE keyType = CKK_DH;
+ CK_ULONG bits = 0;
+ CK_ATTRIBUTE dTemplate[] =
+ {
+ { CKA_CLASS, &dClass, (CK_ULONG) sizeof(dClass) },
+ { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+ { CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_PRIME_BITS, &bits, (CK_ULONG) sizeof(bits) }
+ };
+ CK_ATTRIBUTE pTemplate[] =
+ {
+ { CKA_PRIME, NULL, 0 },
+ { CKA_BASE, NULL, 0 }
+ };
+ CK_OBJECT_HANDLE pub = CK_INVALID_HANDLE;
+ CK_OBJECT_CLASS pubClass = CKO_PUBLIC_KEY;
+ CK_ATTRIBUTE pubTemplate[] =
+ {
+ { CKA_CLASS, &pubClass, (CK_ULONG) sizeof(pubClass) },
+ { CKA_KEY_TYPE,&keyType, (CK_ULONG) sizeof(keyType) },
+ { CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_PRIME, NULL, 0 },
+ { CKA_BASE, NULL, 0 },
+ };
+ CK_OBJECT_HANDLE priv = CK_INVALID_HANDLE;
+ CK_OBJECT_HANDLE privClass = CKO_PRIVATE_KEY;
+ CK_ATTRIBUTE privTemplate[] =
+ {
+ { CKA_CLASS, &privClass, (CK_ULONG) sizeof(privClass) },
+ { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+ { CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_SENSITIVE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_EXTRACTABLE, &truevalue, (CK_ULONG) sizeof(truevalue) },
+ { CKA_DERIVE, &truevalue, (CK_ULONG) sizeof(truevalue) },
+ };
+ CK_ATTRIBUTE *attr;
+ pk11_object_t *dh = NULL;
+ pk11_context_t *pk11_ctx;
+ isc_result_t ret;
+
+ UNUSED(callback);
+
+ pk11_ctx = (pk11_context_t *) isc_mem_get(key->mctx,
+ sizeof(*pk11_ctx));
+ if (pk11_ctx == NULL)
+ return (ISC_R_NOMEMORY);
+ ret = pk11_get_session(pk11_ctx, OP_DH, true, false,
+ false, NULL, pk11_get_best_token(OP_DH));
+ if (ret != ISC_R_SUCCESS)
+ goto err;
+
+ bits = key->key_size;
+ if ((generator == 0) &&
+ ((bits == 768) || (bits == 1024) || (bits == 1536))) {
+ if (bits == 768) {
+ pubTemplate[4].pValue =
+ isc_mem_get(key->mctx, sizeof(pk11_dh_bn768));
+ if (pubTemplate[4].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(pubTemplate[4].pValue,
+ pk11_dh_bn768, sizeof(pk11_dh_bn768));
+ pubTemplate[4].ulValueLen = sizeof(pk11_dh_bn768);
+ } else if (bits == 1024) {
+ pubTemplate[4].pValue =
+ isc_mem_get(key->mctx, sizeof(pk11_dh_bn1024));
+ if (pubTemplate[4].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(pubTemplate[4].pValue,
+ pk11_dh_bn1024, sizeof(pk11_dh_bn1024));
+ pubTemplate[4].ulValueLen = sizeof(pk11_dh_bn1024);
+ } else {
+ pubTemplate[4].pValue =
+ isc_mem_get(key->mctx, sizeof(pk11_dh_bn1536));
+ if (pubTemplate[4].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(pubTemplate[4].pValue,
+ pk11_dh_bn1536, sizeof(pk11_dh_bn1536));
+ pubTemplate[4].ulValueLen = sizeof(pk11_dh_bn1536);
+ }
+ pubTemplate[5].pValue = isc_mem_get(key->mctx,
+ sizeof(pk11_dh_bn2));
+ if (pubTemplate[5].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(pubTemplate[5].pValue, pk11_dh_bn2,
+ sizeof(pk11_dh_bn2));
+ pubTemplate[5].ulValueLen = sizeof(pk11_dh_bn2);
+ } else {
+ PK11_RET(pkcs_C_GenerateKey,
+ (pk11_ctx->session, &mech,
+ dTemplate, (CK_ULONG) 5, &domainparams),
+ DST_R_CRYPTOFAILURE);
+ PK11_RET(pkcs_C_GetAttributeValue,
+ (pk11_ctx->session, domainparams,
+ pTemplate, (CK_ULONG) 2),
+ DST_R_CRYPTOFAILURE);
+ pTemplate[0].pValue = isc_mem_get(key->mctx,
+ pTemplate[0].ulValueLen);
+ if (pTemplate[0].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(pTemplate[0].pValue, 0, pTemplate[0].ulValueLen);
+ pTemplate[1].pValue = isc_mem_get(key->mctx,
+ pTemplate[1].ulValueLen);
+ if (pTemplate[1].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(pTemplate[1].pValue, 0, pTemplate[1].ulValueLen);
+ PK11_RET(pkcs_C_GetAttributeValue,
+ (pk11_ctx->session, domainparams,
+ pTemplate, (CK_ULONG) 2),
+ DST_R_CRYPTOFAILURE);
+
+ pubTemplate[4].pValue = pTemplate[0].pValue;
+ pubTemplate[4].ulValueLen = pTemplate[0].ulValueLen;
+ pTemplate[0].pValue = NULL;
+ pubTemplate[5].pValue = pTemplate[1].pValue;
+ pubTemplate[5].ulValueLen = pTemplate[1].ulValueLen;
+ pTemplate[1].pValue = NULL;
+ }
+
+ mech.mechanism = CKM_DH_PKCS_KEY_PAIR_GEN;
+ PK11_RET(pkcs_C_GenerateKeyPair,
+ (pk11_ctx->session, &mech,
+ pubTemplate, (CK_ULONG) 6,
+ privTemplate, (CK_ULONG) 7,
+ &pub, &priv),
+ DST_R_CRYPTOFAILURE);
+
+ dh = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*dh));
+ if (dh == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(dh, 0, sizeof(*dh));
+ key->keydata.pkey = dh;
+ dh->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 4);
+ if (dh->repr == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(dh->repr, 0, sizeof(*attr) * 4);
+ dh->attrcnt = 4;
+
+ attr = dh->repr;
+ attr[0].type = CKA_PRIME;
+ attr[0].pValue = pubTemplate[4].pValue;
+ attr[0].ulValueLen = pubTemplate[4].ulValueLen;
+ pubTemplate[4].pValue = NULL;
+
+ attr[1].type = CKA_BASE;
+ attr[1].pValue = pubTemplate[5].pValue;
+ attr[1].ulValueLen = pubTemplate[5].ulValueLen;
+ pubTemplate[5].pValue =NULL;
+
+ attr += 2;
+ attr->type = CKA_VALUE;
+ PK11_RET(pkcs_C_GetAttributeValue,
+ (pk11_ctx->session, pub, attr, 1),
+ DST_R_CRYPTOFAILURE);
+ attr->pValue = isc_mem_get(key->mctx, attr->ulValueLen);
+ if (attr->pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(attr->pValue, 0, attr->ulValueLen);
+ PK11_RET(pkcs_C_GetAttributeValue,
+ (pk11_ctx->session, pub, attr, 1),
+ DST_R_CRYPTOFAILURE);
+
+ attr++;
+ attr->type = CKA_VALUE;
+ PK11_RET(pkcs_C_GetAttributeValue,
+ (pk11_ctx->session, priv, attr, 1),
+ DST_R_CRYPTOFAILURE);
+ attr->pValue = isc_mem_get(key->mctx, attr->ulValueLen);
+ if (attr->pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(attr->pValue, 0, attr->ulValueLen);
+ PK11_RET(pkcs_C_GetAttributeValue,
+ (pk11_ctx->session, priv, attr, 1),
+ DST_R_CRYPTOFAILURE);
+ attr->type = CKA_VALUE2;
+
+ (void) pkcs_C_DestroyObject(pk11_ctx->session, priv);
+ (void) pkcs_C_DestroyObject(pk11_ctx->session, pub);
+ (void) pkcs_C_DestroyObject(pk11_ctx->session, domainparams);
+ pk11_return_session(pk11_ctx);
+ isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx));
+ isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+ return (ISC_R_SUCCESS);
+
+ err:
+ pkcs11dh_destroy(key);
+ if (priv != CK_INVALID_HANDLE)
+ (void) pkcs_C_DestroyObject(pk11_ctx->session, priv);
+ if (pub != CK_INVALID_HANDLE)
+ (void) pkcs_C_DestroyObject(pk11_ctx->session, pub);
+ if (domainparams != CK_INVALID_HANDLE)
+ (void) pkcs_C_DestroyObject(pk11_ctx->session, domainparams);
+
+ if (pubTemplate[4].pValue != NULL) {
+ isc_safe_memwipe(pubTemplate[4].pValue,
+ pubTemplate[4].ulValueLen);
+ isc_mem_put(key->mctx,
+ pubTemplate[4].pValue,
+ pubTemplate[4].ulValueLen);
+ }
+ if (pubTemplate[5].pValue != NULL) {
+ isc_safe_memwipe(pubTemplate[5].pValue,
+ pubTemplate[5].ulValueLen);
+ isc_mem_put(key->mctx,
+ pubTemplate[5].pValue,
+ pubTemplate[5].ulValueLen);
+ }
+ if (pTemplate[0].pValue != NULL) {
+ isc_safe_memwipe(pTemplate[0].pValue,
+ pTemplate[0].ulValueLen);
+ isc_mem_put(key->mctx,
+ pTemplate[0].pValue,
+ pTemplate[0].ulValueLen);
+ }
+ if (pTemplate[1].pValue != NULL) {
+ isc_safe_memwipe(pTemplate[1].pValue,
+ pTemplate[1].ulValueLen);
+ isc_mem_put(key->mctx,
+ pTemplate[1].pValue,
+ pTemplate[1].ulValueLen);
+ }
+
+ pk11_return_session(pk11_ctx);
+ isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx));
+ isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+ return (ret);
+}
+
+static bool
+pkcs11dh_isprivate(const dst_key_t *key) {
+ pk11_object_t *dh = key->keydata.pkey;
+ CK_ATTRIBUTE *attr;
+
+ if (dh == NULL)
+ return (false);
+ attr = pk11_attribute_bytype(dh, CKA_VALUE2);
+ return (attr != NULL || dh->ontoken);
+}
+
+static void
+pkcs11dh_destroy(dst_key_t *key) {
+ pk11_object_t *dh = key->keydata.pkey;
+ CK_ATTRIBUTE *attr;
+
+ if (dh == NULL)
+ return;
+
+ INSIST((dh->object == CK_INVALID_HANDLE) || dh->ontoken);
+
+ for (attr = pk11_attribute_first(dh);
+ attr != NULL;
+ attr = pk11_attribute_next(dh, attr))
+ switch (attr->type) {
+ case CKA_VALUE:
+ case CKA_VALUE2:
+ case CKA_PRIME:
+ case CKA_BASE:
+ if (attr->pValue != NULL) {
+ isc_safe_memwipe(attr->pValue,
+ attr->ulValueLen);
+ isc_mem_put(key->mctx,
+ attr->pValue,
+ attr->ulValueLen);
+ }
+ break;
+ }
+ if (dh->repr != NULL) {
+ isc_safe_memwipe(dh->repr, dh->attrcnt * sizeof(*attr));
+ isc_mem_put(key->mctx, dh->repr, dh->attrcnt * sizeof(*attr));
+ }
+ isc_safe_memwipe(dh, sizeof(*dh));
+ isc_mem_put(key->mctx, dh, sizeof(*dh));
+ key->keydata.pkey = NULL;
+}
+
+static void
+uint16_toregion(uint16_t val, isc_region_t *region) {
+ *region->base = (val & 0xff00) >> 8;
+ isc_region_consume(region, 1);
+ *region->base = (val & 0x00ff);
+ isc_region_consume(region, 1);
+}
+
+static uint16_t
+uint16_fromregion(isc_region_t *region) {
+ uint16_t val;
+ unsigned char *cp = region->base;
+
+ val = ((unsigned int)(cp[0])) << 8;
+ val |= ((unsigned int)(cp[1]));
+
+ isc_region_consume(region, 2);
+
+ return (val);
+}
+
+static isc_result_t
+pkcs11dh_todns(const dst_key_t *key, isc_buffer_t *data) {
+ pk11_object_t *dh;
+ CK_ATTRIBUTE *attr;
+ isc_region_t r;
+ uint16_t dnslen, plen = 0, glen = 0, publen = 0;
+ CK_BYTE *prime = NULL, *base = NULL, *pub = NULL;
+
+ REQUIRE(key->keydata.pkey != NULL);
+
+ dh = key->keydata.pkey;
+
+ for (attr = pk11_attribute_first(dh);
+ attr != NULL;
+ attr = pk11_attribute_next(dh, attr))
+ switch (attr->type) {
+ case CKA_VALUE:
+ pub = (CK_BYTE *) attr->pValue;
+ publen = (uint16_t) attr->ulValueLen;
+ break;
+ case CKA_PRIME:
+ prime = (CK_BYTE *) attr->pValue;
+ plen = (uint16_t) attr->ulValueLen;
+ break;
+ case CKA_BASE:
+ base = (CK_BYTE *) attr->pValue;
+ glen = (uint16_t) attr->ulValueLen;
+ break;
+ }
+ REQUIRE((prime != NULL) && (base != NULL) && (pub != NULL));
+
+ isc_buffer_availableregion(data, &r);
+
+ if ((glen == 1) && isc_safe_memequal(pk11_dh_bn2, base, glen) &&
+ (((plen == sizeof(pk11_dh_bn768)) &&
+ isc_safe_memequal(pk11_dh_bn768, prime, plen)) ||
+ ((plen == sizeof(pk11_dh_bn1024)) &&
+ isc_safe_memequal(pk11_dh_bn1024, prime, plen)) ||
+ ((plen == sizeof(pk11_dh_bn1536)) &&
+ isc_safe_memequal(pk11_dh_bn1536, prime, plen)))) {
+ plen = 1;
+ glen = 0;
+ }
+
+ dnslen = plen + glen + publen + 6;
+ if (r.length < (unsigned int) dnslen)
+ return (ISC_R_NOSPACE);
+
+ uint16_toregion(plen, &r);
+ if (plen == 1) {
+ if (isc_safe_memequal(pk11_dh_bn768, prime,
+ sizeof(pk11_dh_bn768)))
+ *r.base = 1;
+ else if (isc_safe_memequal(pk11_dh_bn1024, prime,
+ sizeof(pk11_dh_bn1024)))
+ *r.base = 2;
+ else
+ *r.base = 3;
+ }
+ else
+ memmove(r.base, prime, plen);
+ isc_region_consume(&r, plen);
+
+ uint16_toregion(glen, &r);
+ if (glen > 0)
+ memmove(r.base, base, glen);
+ isc_region_consume(&r, glen);
+
+ uint16_toregion(publen, &r);
+ memmove(r.base, pub, publen);
+ isc_region_consume(&r, publen);
+
+ isc_buffer_add(data, dnslen);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+pkcs11dh_fromdns(dst_key_t *key, isc_buffer_t *data) {
+ pk11_object_t *dh = NULL;
+ isc_region_t r;
+ uint16_t plen, glen, plen_, glen_, publen;
+ CK_BYTE *prime = NULL, *base = NULL, *pub = NULL;
+ CK_ATTRIBUTE *attr;
+ int special = 0;
+ isc_result_t result;
+
+ isc_buffer_remainingregion(data, &r);
+ if (r.length == 0) {
+ result = ISC_R_SUCCESS;
+ goto cleanup;
+ }
+
+ dh = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*dh));
+ if (dh == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+
+ memset(dh, 0, sizeof(*dh));
+ result = DST_R_INVALIDPUBLICKEY;
+
+ /*
+ * Read the prime length. 1 & 2 are table entries, > 16 means a
+ * prime follows, otherwise an error.
+ */
+ if (r.length < 2)
+ goto cleanup;
+
+ plen = uint16_fromregion(&r);
+ if (plen < 16 && plen != 1 && plen != 2)
+ goto cleanup;
+
+ if (r.length < plen)
+ goto cleanup;
+
+ plen_ = plen;
+ if (plen == 1 || plen == 2) {
+ if (plen == 1) {
+ special = *r.base;
+ isc_region_consume(&r, 1);
+ } else {
+ special = uint16_fromregion(&r);
+ }
+ switch (special) {
+ case 1:
+ prime = pk11_dh_bn768;
+ plen_ = sizeof(pk11_dh_bn768);
+ break;
+ case 2:
+ prime = pk11_dh_bn1024;
+ plen_ = sizeof(pk11_dh_bn1024);
+ break;
+ case 3:
+ prime = pk11_dh_bn1536;
+ plen_ = sizeof(pk11_dh_bn1536);
+ break;
+ default:
+ goto cleanup;
+ }
+ }
+ else {
+ prime = r.base;
+ isc_region_consume(&r, 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)
+ goto cleanup;
+
+ glen = uint16_fromregion(&r);
+ if (r.length < glen)
+ goto cleanup;
+
+ glen_ = glen;
+ if (special != 0) {
+ if (glen == 0) {
+ base = pk11_dh_bn2;
+ glen_ = sizeof(pk11_dh_bn2);
+ }
+ else {
+ base = r.base;
+ if (!isc_safe_memequal(base, pk11_dh_bn2, glen))
+ goto cleanup;
+ base = pk11_dh_bn2;
+ glen_ = sizeof(pk11_dh_bn2);
+ }
+ }
+ else {
+ if (glen == 0)
+ goto cleanup;
+ base = r.base;
+ }
+ isc_region_consume(&r, glen);
+
+ if (r.length < 2)
+ goto cleanup;
+
+ publen = uint16_fromregion(&r);
+ if (r.length < publen)
+ goto cleanup;
+
+ pub = r.base;
+ isc_region_consume(&r, publen);
+
+ key->key_size = pk11_numbits(prime, plen_);
+
+ dh->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 3);
+ if (dh->repr == NULL)
+ goto nomemory;
+ memset(dh->repr, 0, sizeof(*attr) * 3);
+ dh->attrcnt = 3;
+
+ attr = dh->repr;
+ attr[0].type = CKA_PRIME;
+ attr[0].pValue = isc_mem_get(key->mctx, plen_);
+ if (attr[0].pValue == NULL)
+ goto nomemory;
+ memmove(attr[0].pValue, prime, plen_);
+ attr[0].ulValueLen = (CK_ULONG) plen_;
+
+ attr[1].type = CKA_BASE;
+ attr[1].pValue = isc_mem_get(key->mctx, glen_);
+ if (attr[1].pValue == NULL)
+ goto nomemory;
+ memmove(attr[1].pValue, base, glen_);
+ attr[1].ulValueLen = (CK_ULONG) glen_;
+
+ attr[2].type = CKA_VALUE;
+ attr[2].pValue = isc_mem_get(key->mctx, publen);
+ if (attr[2].pValue == NULL)
+ goto nomemory;
+ memmove(attr[2].pValue, pub, publen);
+ attr[2].ulValueLen = (CK_ULONG) publen;
+
+ isc_buffer_forward(data, plen + glen + publen + 6);
+
+ key->keydata.pkey = dh;
+
+ return (ISC_R_SUCCESS);
+
+ nomemory:
+ for (attr = pk11_attribute_first(dh);
+ attr != NULL;
+ attr = pk11_attribute_next(dh, attr))
+ switch (attr->type) {
+ case CKA_VALUE:
+ case CKA_PRIME:
+ case CKA_BASE:
+ if (attr->pValue != NULL) {
+ isc_safe_memwipe(attr->pValue,
+ attr->ulValueLen);
+ isc_mem_put(key->mctx,
+ attr->pValue,
+ attr->ulValueLen);
+ }
+ break;
+ }
+ if (dh->repr != NULL) {
+ isc_safe_memwipe(dh->repr, dh->attrcnt * sizeof(*attr));
+ isc_mem_put(key->mctx, dh->repr, dh->attrcnt * sizeof(*attr));
+ }
+
+ result = ISC_R_NOMEMORY;
+
+ cleanup:
+ if (dh != NULL) {
+ isc_safe_memwipe(dh, sizeof(*dh));
+ isc_mem_put(key->mctx, dh, sizeof(*dh));
+ }
+ return (result);
+}
+
+static isc_result_t
+pkcs11dh_tofile(const dst_key_t *key, const char *directory) {
+ int i;
+ pk11_object_t *dh;
+ CK_ATTRIBUTE *attr;
+ CK_ATTRIBUTE *prime = NULL, *base = NULL, *pub = NULL, *prv = NULL;
+ dst_private_t priv;
+ unsigned char *bufs[4];
+ isc_result_t result;
+
+ if (key->keydata.pkey == NULL)
+ return (DST_R_NULLKEY);
+
+ if (key->external)
+ return (DST_R_EXTERNALKEY);
+
+ dh = key->keydata.pkey;
+
+ for (attr = pk11_attribute_first(dh);
+ attr != NULL;
+ attr = pk11_attribute_next(dh, attr))
+ switch (attr->type) {
+ case CKA_VALUE:
+ pub = attr;
+ break;
+ case CKA_VALUE2:
+ prv = attr;
+ break;
+ case CKA_PRIME:
+ prime = attr;
+ break;
+ case CKA_BASE:
+ base = attr;
+ break;
+ }
+ if ((prime == NULL) || (base == NULL) ||
+ (pub == NULL) || (prv == NULL))
+ return (DST_R_NULLKEY);
+
+ memset(bufs, 0, sizeof(bufs));
+ for (i = 0; i < 4; i++) {
+ bufs[i] = isc_mem_get(key->mctx, prime->ulValueLen);
+ if (bufs[i] == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto fail;
+ }
+ memset(bufs[i], 0, prime->ulValueLen);
+ }
+
+ i = 0;
+
+ priv.elements[i].tag = TAG_DH_PRIME;
+ priv.elements[i].length = (unsigned short) prime->ulValueLen;
+ memmove(bufs[i], prime->pValue, prime->ulValueLen);
+ priv.elements[i].data = bufs[i];
+ i++;
+
+ priv.elements[i].tag = TAG_DH_GENERATOR;
+ priv.elements[i].length = (unsigned short) base->ulValueLen;
+ memmove(bufs[i], base->pValue, base->ulValueLen);
+ priv.elements[i].data = bufs[i];
+ i++;
+
+ priv.elements[i].tag = TAG_DH_PRIVATE;
+ priv.elements[i].length = (unsigned short) prv->ulValueLen;
+ memmove(bufs[i], prv->pValue, prv->ulValueLen);
+ priv.elements[i].data = bufs[i];
+ i++;
+
+ priv.elements[i].tag = TAG_DH_PUBLIC;
+ priv.elements[i].length = (unsigned short) pub->ulValueLen;
+ memmove(bufs[i], pub->pValue, pub->ulValueLen);
+ 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_safe_memwipe(bufs[i], prime->ulValueLen);
+ isc_mem_put(key->mctx, bufs[i], prime->ulValueLen);
+ }
+ return (result);
+}
+
+static isc_result_t
+pkcs11dh_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
+ dst_private_t priv;
+ isc_result_t ret;
+ int i;
+ pk11_object_t *dh = NULL;
+ CK_ATTRIBUTE *attr;
+ isc_mem_t *mctx;
+
+ UNUSED(pub);
+ 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);
+
+ if (key->external)
+ DST_RET(DST_R_EXTERNALKEY);
+
+ dh = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*dh));
+ if (dh == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(dh, 0, sizeof(*dh));
+ key->keydata.pkey = dh;
+ dh->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 4);
+ if (dh->repr == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(dh->repr, 0, sizeof(*attr) * 4);
+ dh->attrcnt = 4;
+ attr = dh->repr;
+ attr[0].type = CKA_PRIME;
+ attr[1].type = CKA_BASE;
+ attr[2].type = CKA_VALUE;
+ attr[3].type = CKA_VALUE2;
+
+ for (i = 0; i < priv.nelements; i++) {
+ CK_BYTE *bn;
+
+ bn = isc_mem_get(key->mctx, priv.elements[i].length);
+ if (bn == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(bn, priv.elements[i].data, priv.elements[i].length);
+
+ switch (priv.elements[i].tag) {
+ case TAG_DH_PRIME:
+ attr = pk11_attribute_bytype(dh, CKA_PRIME);
+ INSIST(attr != NULL);
+ attr->pValue = bn;
+ attr->ulValueLen = priv.elements[i].length;
+ break;
+ case TAG_DH_GENERATOR:
+ attr = pk11_attribute_bytype(dh, CKA_BASE);
+ INSIST(attr != NULL);
+ attr->pValue = bn;
+ attr->ulValueLen = priv.elements[i].length;
+ break;
+ case TAG_DH_PRIVATE:
+ attr = pk11_attribute_bytype(dh, CKA_VALUE2);
+ INSIST(attr != NULL);
+ attr->pValue = bn;
+ attr->ulValueLen = priv.elements[i].length;
+ break;
+ case TAG_DH_PUBLIC:
+ attr = pk11_attribute_bytype(dh, CKA_VALUE);
+ INSIST(attr != NULL);
+ attr->pValue = bn;
+ attr->ulValueLen = priv.elements[i].length;
+ break;
+ }
+ }
+ dst__privstruct_free(&priv, mctx);
+
+ attr = pk11_attribute_bytype(dh, CKA_PRIME);
+ INSIST(attr != NULL);
+ key->key_size = pk11_numbits(attr->pValue, attr->ulValueLen);
+
+ return (ISC_R_SUCCESS);
+
+ err:
+ pkcs11dh_destroy(key);
+ dst__privstruct_free(&priv, mctx);
+ isc_safe_memwipe(&priv, sizeof(priv));
+ return (ret);
+}
+
+static dst_func_t pkcs11dh_functions = {
+ NULL, /*%< createctx */
+ NULL, /*%< createctx2 */
+ NULL, /*%< destroyctx */
+ NULL, /*%< adddata */
+ NULL, /*%< sign */
+ NULL, /*%< verify */
+ NULL, /*%< verify2 */
+ pkcs11dh_computesecret,
+ pkcs11dh_compare,
+ pkcs11dh_paramcompare,
+ pkcs11dh_generate,
+ pkcs11dh_isprivate,
+ pkcs11dh_destroy,
+ pkcs11dh_todns,
+ pkcs11dh_fromdns,
+ pkcs11dh_tofile,
+ pkcs11dh_parse,
+ NULL, /*%< cleanup */
+ NULL, /*%< fromlabel */
+ NULL, /*%< dump */
+ NULL, /*%< restore */
+};
+
+isc_result_t
+dst__pkcs11dh_init(dst_func_t **funcp) {
+ REQUIRE(funcp != NULL);
+ if (*funcp == NULL)
+ *funcp = &pkcs11dh_functions;
+ return (ISC_R_SUCCESS);
+}
+#endif /* !PK11_DH_DISABLE */
+
+#else /* PKCS11CRYPTO */
+
+#include <isc/util.h>
+
+EMPTY_TRANSLATION_UNIT
+
+#endif /* PKCS11CRYPTO */
+/*! \file */
diff --git a/lib/dns/pkcs11dsa_link.c b/lib/dns/pkcs11dsa_link.c
new file mode 100644
index 0000000..12d707a
--- /dev/null
+++ b/lib/dns/pkcs11dsa_link.c
@@ -0,0 +1,1126 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifdef PKCS11CRYPTO
+
+#include <config.h>
+
+#include <pk11/site.h>
+
+#ifndef PK11_DSA_DISABLE
+
+#include <string.h>
+#include <stdbool.h>
+
+#include <isc/entropy.h>
+#include <isc/mem.h>
+#include <isc/safe.h>
+#include <isc/sha1.h>
+#include <isc/util.h>
+
+#include <dst/result.h>
+
+#include "dst_internal.h"
+#include "dst_parse.h"
+#include "dst_pkcs11.h"
+
+#include <pk11/internal.h>
+
+/*
+ * FIPS 186-2 DSA keys:
+ * mechanisms:
+ * CKM_DSA_SHA1,
+ * CKM_DSA_KEY_PAIR_GEN,
+ * CKM_DSA_PARAMETER_GEN
+ * domain parameters:
+ * object class CKO_DOMAIN_PARAMETERS
+ * key type CKK_DSA
+ * attribute CKA_PRIME (prime p)
+ * attribute CKA_SUBPRIME (subprime q)
+ * attribute CKA_BASE (base g)
+ * optional attribute CKA_PRIME_BITS (p length in bits)
+ * public keys:
+ * object class CKO_PUBLIC_KEY
+ * key type CKK_DSA
+ * attribute CKA_PRIME (prime p)
+ * attribute CKA_SUBPRIME (subprime q)
+ * attribute CKA_BASE (base g)
+ * attribute CKA_VALUE (public value y)
+ * private keys:
+ * object class CKO_PRIVATE_KEY
+ * key type CKK_DSA
+ * attribute CKA_PRIME (prime p)
+ * attribute CKA_SUBPRIME (subprime q)
+ * attribute CKA_BASE (base g)
+ * attribute CKA_VALUE (private value x)
+ * reuse CKA_PRIVATE_EXPONENT for key pair private value
+ */
+
+#define CKA_VALUE2 CKA_PRIVATE_EXPONENT
+
+#define DST_RET(a) {ret = a; goto err;}
+
+static CK_BBOOL truevalue = TRUE;
+static CK_BBOOL falsevalue = FALSE;
+
+static isc_result_t pkcs11dsa_todns(const dst_key_t *key, isc_buffer_t *data);
+static void pkcs11dsa_destroy(dst_key_t *key);
+
+static isc_result_t
+pkcs11dsa_createctx_sign(dst_key_t *key, dst_context_t *dctx) {
+ CK_RV rv;
+ CK_MECHANISM mech = { CKM_DSA_SHA1, NULL, 0 };
+ CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY;
+ CK_KEY_TYPE keyType = CKK_DSA;
+ CK_ATTRIBUTE keyTemplate[] =
+ {
+ { CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+ { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+ { CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_SENSITIVE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_SIGN, &truevalue, (CK_ULONG) sizeof(truevalue) },
+ { CKA_PRIME, NULL, 0 },
+ { CKA_SUBPRIME, NULL, 0 },
+ { CKA_BASE, NULL, 0 },
+ { CKA_VALUE, NULL, 0 }
+ };
+ CK_ATTRIBUTE *attr;
+ pk11_object_t *dsa;
+ pk11_context_t *pk11_ctx;
+ isc_result_t ret;
+ unsigned int i;
+
+ REQUIRE(key != NULL);
+ dsa = key->keydata.pkey;
+ REQUIRE(dsa != NULL);
+
+ pk11_ctx = (pk11_context_t *) isc_mem_get(dctx->mctx,
+ sizeof(*pk11_ctx));
+ if (pk11_ctx == NULL)
+ return (ISC_R_NOMEMORY);
+ ret = pk11_get_session(pk11_ctx, OP_DSA, true, false,
+ dsa->reqlogon, NULL,
+ pk11_get_best_token(OP_DSA));
+ if (ret != ISC_R_SUCCESS)
+ goto err;
+
+ if (dsa->ontoken && (dsa->object != CK_INVALID_HANDLE)) {
+ pk11_ctx->ontoken = dsa->ontoken;
+ pk11_ctx->object = dsa->object;
+ goto token_key;
+ }
+
+ for (attr = pk11_attribute_first(dsa);
+ attr != NULL;
+ attr = pk11_attribute_next(dsa, attr))
+ switch (attr->type) {
+ case CKA_PRIME:
+ INSIST(keyTemplate[6].type == attr->type);
+ keyTemplate[6].pValue = isc_mem_get(dctx->mctx,
+ attr->ulValueLen);
+ if (keyTemplate[6].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[6].pValue, attr->pValue,
+ attr->ulValueLen);
+ keyTemplate[6].ulValueLen = attr->ulValueLen;
+ break;
+ case CKA_SUBPRIME:
+ INSIST(keyTemplate[7].type == attr->type);
+ keyTemplate[7].pValue = isc_mem_get(dctx->mctx,
+ attr->ulValueLen);
+ if (keyTemplate[7].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[7].pValue, attr->pValue,
+ attr->ulValueLen);
+ keyTemplate[7].ulValueLen = attr->ulValueLen;
+ break;
+ case CKA_BASE:
+ INSIST(keyTemplate[8].type == attr->type);
+ keyTemplate[8].pValue = isc_mem_get(dctx->mctx,
+ attr->ulValueLen);
+ if (keyTemplate[8].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[8].pValue, attr->pValue,
+ attr->ulValueLen);
+ keyTemplate[8].ulValueLen = attr->ulValueLen;
+ break;
+ case CKA_VALUE2:
+ INSIST(keyTemplate[9].type == CKA_VALUE);
+ keyTemplate[9].pValue = isc_mem_get(dctx->mctx,
+ attr->ulValueLen);
+ if (keyTemplate[9].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[9].pValue, attr->pValue,
+ attr->ulValueLen);
+ keyTemplate[9].ulValueLen = attr->ulValueLen;
+ break;
+ }
+ pk11_ctx->object = CK_INVALID_HANDLE;
+ pk11_ctx->ontoken = false;
+ PK11_RET(pkcs_C_CreateObject,
+ (pk11_ctx->session,
+ keyTemplate, (CK_ULONG) 10,
+ &pk11_ctx->object),
+ ISC_R_FAILURE);
+
+ token_key:
+
+ PK11_RET(pkcs_C_SignInit,
+ (pk11_ctx->session, &mech, pk11_ctx->object),
+ ISC_R_FAILURE);
+
+ dctx->ctxdata.pk11_ctx = pk11_ctx;
+
+ for (i = 6; i <= 9; i++)
+ if (keyTemplate[i].pValue != NULL) {
+ isc_safe_memwipe(keyTemplate[i].pValue,
+ keyTemplate[i].ulValueLen);
+ isc_mem_put(dctx->mctx,
+ keyTemplate[i].pValue,
+ keyTemplate[i].ulValueLen);
+ }
+
+ return (ISC_R_SUCCESS);
+
+ err:
+ if (!pk11_ctx->ontoken && (pk11_ctx->object != CK_INVALID_HANDLE))
+ (void) pkcs_C_DestroyObject(pk11_ctx->session, pk11_ctx->object);
+ for (i = 6; i <= 9; i++)
+ if (keyTemplate[i].pValue != NULL) {
+ isc_safe_memwipe(keyTemplate[i].pValue,
+ keyTemplate[i].ulValueLen);
+ isc_mem_put(dctx->mctx,
+ keyTemplate[i].pValue,
+ keyTemplate[i].ulValueLen);
+ }
+ pk11_return_session(pk11_ctx);
+ isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx));
+ isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+ return (ret);
+}
+
+static isc_result_t
+pkcs11dsa_createctx_verify(dst_key_t *key, dst_context_t *dctx) {
+ CK_RV rv;
+ CK_MECHANISM mech = { CKM_DSA_SHA1, NULL, 0 };
+ CK_OBJECT_CLASS keyClass = CKO_PUBLIC_KEY;
+ CK_KEY_TYPE keyType = CKK_DSA;
+ CK_ATTRIBUTE keyTemplate[] =
+ {
+ { CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+ { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+ { CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_VERIFY, &truevalue, (CK_ULONG) sizeof(truevalue) },
+ { CKA_PRIME, NULL, 0 },
+ { CKA_SUBPRIME, NULL, 0 },
+ { CKA_BASE, NULL, 0 },
+ { CKA_VALUE, NULL, 0 }
+ };
+ CK_ATTRIBUTE *attr;
+ pk11_object_t *dsa;
+ pk11_context_t *pk11_ctx;
+ isc_result_t ret;
+ unsigned int i;
+
+ dsa = key->keydata.pkey;
+ REQUIRE(dsa != NULL);
+ pk11_ctx = (pk11_context_t *) isc_mem_get(dctx->mctx,
+ sizeof(*pk11_ctx));
+ if (pk11_ctx == NULL)
+ return (ISC_R_NOMEMORY);
+ ret = pk11_get_session(pk11_ctx, OP_DSA, true, false,
+ dsa->reqlogon, NULL,
+ pk11_get_best_token(OP_DSA));
+ if (ret != ISC_R_SUCCESS)
+ goto err;
+
+ if (dsa->ontoken && (dsa->object != CK_INVALID_HANDLE)) {
+ pk11_ctx->ontoken = dsa->ontoken;
+ pk11_ctx->object = dsa->object;
+ goto token_key;
+ }
+
+ for (attr = pk11_attribute_first(dsa);
+ attr != NULL;
+ attr = pk11_attribute_next(dsa, attr))
+ switch (attr->type) {
+ case CKA_PRIME:
+ INSIST(keyTemplate[5].type == attr->type);
+ keyTemplate[5].pValue = isc_mem_get(dctx->mctx,
+ attr->ulValueLen);
+ if (keyTemplate[5].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[5].pValue, attr->pValue,
+ attr->ulValueLen);
+ keyTemplate[5].ulValueLen = attr->ulValueLen;
+ break;
+ case CKA_SUBPRIME:
+ INSIST(keyTemplate[6].type == attr->type);
+ keyTemplate[6].pValue = isc_mem_get(dctx->mctx,
+ attr->ulValueLen);
+ if (keyTemplate[6].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[6].pValue, attr->pValue,
+ attr->ulValueLen);
+ keyTemplate[6].ulValueLen = attr->ulValueLen;
+ break;
+ case CKA_BASE:
+ INSIST(keyTemplate[7].type == attr->type);
+ keyTemplate[7].pValue = isc_mem_get(dctx->mctx,
+ attr->ulValueLen);
+ if (keyTemplate[7].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[7].pValue, attr->pValue,
+ attr->ulValueLen);
+ keyTemplate[7].ulValueLen = attr->ulValueLen;
+ break;
+ case CKA_VALUE:
+ INSIST(keyTemplate[8].type == attr->type);
+ keyTemplate[8].pValue = isc_mem_get(dctx->mctx,
+ attr->ulValueLen);
+ if (keyTemplate[8].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[8].pValue, attr->pValue,
+ attr->ulValueLen);
+ keyTemplate[8].ulValueLen = attr->ulValueLen;
+ break;
+ }
+ pk11_ctx->object = CK_INVALID_HANDLE;
+ pk11_ctx->ontoken = false;
+ PK11_RET(pkcs_C_CreateObject,
+ (pk11_ctx->session,
+ keyTemplate, (CK_ULONG) 9,
+ &pk11_ctx->object),
+ ISC_R_FAILURE);
+
+ token_key:
+
+ PK11_RET(pkcs_C_VerifyInit,
+ (pk11_ctx->session, &mech, pk11_ctx->object),
+ ISC_R_FAILURE);
+
+ dctx->ctxdata.pk11_ctx = pk11_ctx;
+
+ for (i = 5; i <= 8; i++)
+ if (keyTemplate[i].pValue != NULL) {
+ isc_safe_memwipe(keyTemplate[i].pValue,
+ keyTemplate[i].ulValueLen);
+ isc_mem_put(dctx->mctx,
+ keyTemplate[i].pValue,
+ keyTemplate[i].ulValueLen);
+ }
+
+ return (ISC_R_SUCCESS);
+
+ err:
+ if (!pk11_ctx->ontoken && (pk11_ctx->object != CK_INVALID_HANDLE))
+ (void) pkcs_C_DestroyObject(pk11_ctx->session, pk11_ctx->object);
+ for (i = 5; i <= 8; i++)
+ if (keyTemplate[i].pValue != NULL) {
+ isc_safe_memwipe(keyTemplate[i].pValue,
+ keyTemplate[i].ulValueLen);
+ isc_mem_put(dctx->mctx,
+ keyTemplate[i].pValue,
+ keyTemplate[i].ulValueLen);
+ }
+ pk11_return_session(pk11_ctx);
+ isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx));
+ isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+ return (ret);
+}
+
+static isc_result_t
+pkcs11dsa_createctx(dst_key_t *key, dst_context_t *dctx) {
+ if (dctx->use == DO_SIGN)
+ return (pkcs11dsa_createctx_sign(key, dctx));
+ else
+ return (pkcs11dsa_createctx_verify(key, dctx));
+}
+
+static void
+pkcs11dsa_destroyctx(dst_context_t *dctx) {
+ pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx;
+
+ if (pk11_ctx != NULL) {
+ if (!pk11_ctx->ontoken &&
+ (pk11_ctx->object != CK_INVALID_HANDLE))
+ (void) pkcs_C_DestroyObject(pk11_ctx->session,
+ pk11_ctx->object);
+ pk11_return_session(pk11_ctx);
+ isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx));
+ isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx));
+ dctx->ctxdata.pk11_ctx = NULL;
+ }
+}
+
+static isc_result_t
+pkcs11dsa_adddata(dst_context_t *dctx, const isc_region_t *data) {
+ CK_RV rv;
+ pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx;
+ isc_result_t ret = ISC_R_SUCCESS;
+
+ if (dctx->use == DO_SIGN)
+ PK11_CALL(pkcs_C_SignUpdate,
+ (pk11_ctx->session,
+ (CK_BYTE_PTR) data->base,
+ (CK_ULONG) data->length),
+ ISC_R_FAILURE);
+ else
+ PK11_CALL(pkcs_C_VerifyUpdate,
+ (pk11_ctx->session,
+ (CK_BYTE_PTR) data->base,
+ (CK_ULONG) data->length),
+ ISC_R_FAILURE);
+ return (ret);
+}
+
+static isc_result_t
+pkcs11dsa_sign(dst_context_t *dctx, isc_buffer_t *sig) {
+ CK_RV rv;
+ CK_ULONG siglen = ISC_SHA1_DIGESTLENGTH * 2;
+ isc_region_t r;
+ pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx;
+ isc_result_t ret = ISC_R_SUCCESS;
+ unsigned int klen;
+
+ isc_buffer_availableregion(sig, &r);
+ if (r.length < ISC_SHA1_DIGESTLENGTH * 2 + 1)
+ return (ISC_R_NOSPACE);
+
+ PK11_RET(pkcs_C_SignFinal,
+ (pk11_ctx->session, (CK_BYTE_PTR) r.base + 1, &siglen),
+ DST_R_SIGNFAILURE);
+ if (siglen != ISC_SHA1_DIGESTLENGTH * 2)
+ return (DST_R_SIGNFAILURE);
+
+ klen = (dctx->key->key_size - 512)/64;
+ if (klen > 255)
+ return (ISC_R_FAILURE);
+ *r.base = klen;
+ isc_buffer_add(sig, ISC_SHA1_DIGESTLENGTH * 2 + 1);
+
+ err:
+ return (ret);
+}
+
+static isc_result_t
+pkcs11dsa_verify(dst_context_t *dctx, const isc_region_t *sig) {
+ CK_RV rv;
+ pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx;
+ isc_result_t ret = ISC_R_SUCCESS;
+
+ PK11_CALL(pkcs_C_VerifyFinal,
+ (pk11_ctx->session,
+ (CK_BYTE_PTR) sig->base + 1,
+ (CK_ULONG) sig->length - 1),
+ DST_R_VERIFYFAILURE);
+ return (ret);
+}
+
+static bool
+pkcs11dsa_compare(const dst_key_t *key1, const dst_key_t *key2) {
+ pk11_object_t *dsa1, *dsa2;
+ CK_ATTRIBUTE *attr1, *attr2;
+
+ dsa1 = key1->keydata.pkey;
+ dsa2 = key2->keydata.pkey;
+
+ if ((dsa1 == NULL) && (dsa2 == NULL))
+ return (true);
+ else if ((dsa1 == NULL) || (dsa2 == NULL))
+ return (false);
+
+ attr1 = pk11_attribute_bytype(dsa1, CKA_PRIME);
+ attr2 = pk11_attribute_bytype(dsa2, CKA_PRIME);
+ if ((attr1 == NULL) && (attr2 == NULL))
+ return (true);
+ else if ((attr1 == NULL) || (attr2 == NULL) ||
+ (attr1->ulValueLen != attr2->ulValueLen) ||
+ !isc_safe_memequal(attr1->pValue, attr2->pValue,
+ attr1->ulValueLen))
+ return (false);
+
+ attr1 = pk11_attribute_bytype(dsa1, CKA_SUBPRIME);
+ attr2 = pk11_attribute_bytype(dsa2, CKA_SUBPRIME);
+ if ((attr1 == NULL) && (attr2 == NULL))
+ return (true);
+ else if ((attr1 == NULL) || (attr2 == NULL) ||
+ (attr1->ulValueLen != attr2->ulValueLen) ||
+ !isc_safe_memequal(attr1->pValue, attr2->pValue,
+ attr1->ulValueLen))
+ return (false);
+
+ attr1 = pk11_attribute_bytype(dsa1, CKA_BASE);
+ attr2 = pk11_attribute_bytype(dsa2, CKA_BASE);
+ if ((attr1 == NULL) && (attr2 == NULL))
+ return (true);
+ else if ((attr1 == NULL) || (attr2 == NULL) ||
+ (attr1->ulValueLen != attr2->ulValueLen) ||
+ !isc_safe_memequal(attr1->pValue, attr2->pValue,
+ attr1->ulValueLen))
+ return (false);
+
+ attr1 = pk11_attribute_bytype(dsa1, CKA_VALUE);
+ attr2 = pk11_attribute_bytype(dsa2, CKA_VALUE);
+ if ((attr1 == NULL) && (attr2 == NULL))
+ return (true);
+ else if ((attr1 == NULL) || (attr2 == NULL) ||
+ (attr1->ulValueLen != attr2->ulValueLen) ||
+ !isc_safe_memequal(attr1->pValue, attr2->pValue,
+ attr1->ulValueLen))
+ return (false);
+
+ attr1 = pk11_attribute_bytype(dsa1, CKA_VALUE2);
+ attr2 = pk11_attribute_bytype(dsa2, CKA_VALUE2);
+ if (((attr1 != NULL) || (attr2 != NULL)) &&
+ ((attr1 == NULL) || (attr2 == NULL) ||
+ (attr1->ulValueLen != attr2->ulValueLen) ||
+ !isc_safe_memequal(attr1->pValue, attr2->pValue,
+ attr1->ulValueLen)))
+ return (false);
+
+ if (!dsa1->ontoken && !dsa2->ontoken)
+ return (true);
+ else if (dsa1->ontoken || dsa2->ontoken ||
+ (dsa1->object != dsa2->object))
+ return (false);
+
+ return (true);
+}
+
+static isc_result_t
+pkcs11dsa_generate(dst_key_t *key, int unused, void (*callback)(int)) {
+ CK_RV rv;
+ CK_MECHANISM mech = { CKM_DSA_PARAMETER_GEN, NULL, 0 };
+ CK_OBJECT_HANDLE dp = CK_INVALID_HANDLE;
+ CK_OBJECT_CLASS dpClass = CKO_DOMAIN_PARAMETERS;
+ CK_KEY_TYPE keyType = CKK_DSA;
+ CK_ULONG bits = 0;
+ CK_ATTRIBUTE dpTemplate[] =
+ {
+ { CKA_CLASS, &dpClass, (CK_ULONG) sizeof(dpClass) },
+ { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+ { CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_PRIME_BITS, &bits, (CK_ULONG) sizeof(bits) },
+ };
+ CK_OBJECT_HANDLE pub = CK_INVALID_HANDLE;
+ CK_OBJECT_CLASS pubClass = CKO_PUBLIC_KEY;
+ CK_ATTRIBUTE pubTemplate[] =
+ {
+ { CKA_CLASS, &pubClass, (CK_ULONG) sizeof(pubClass) },
+ { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+ { CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_VERIFY, &truevalue, (CK_ULONG) sizeof(truevalue) },
+ { CKA_PRIME, NULL, 0 },
+ { CKA_SUBPRIME, NULL, 0 },
+ { CKA_BASE, NULL, 0 }
+ };
+ CK_OBJECT_HANDLE priv = CK_INVALID_HANDLE;
+ CK_OBJECT_HANDLE privClass = CKO_PRIVATE_KEY;
+ CK_ATTRIBUTE privTemplate[] =
+ {
+ { CKA_CLASS, &privClass, (CK_ULONG) sizeof(privClass) },
+ { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+ { CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_SENSITIVE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_EXTRACTABLE, &truevalue, (CK_ULONG) sizeof(truevalue) },
+ { CKA_SIGN, &truevalue, (CK_ULONG) sizeof(truevalue) },
+ };
+ CK_ATTRIBUTE *attr;
+ pk11_object_t *dsa;
+ pk11_context_t *pk11_ctx;
+ isc_result_t ret;
+ unsigned int i;
+
+ UNUSED(unused);
+ UNUSED(callback);
+
+ pk11_ctx = (pk11_context_t *) isc_mem_get(key->mctx,
+ sizeof(*pk11_ctx));
+ if (pk11_ctx == NULL)
+ return (ISC_R_NOMEMORY);
+ ret = pk11_get_session(pk11_ctx, OP_DSA, true, false,
+ false, NULL, pk11_get_best_token(OP_DSA));
+ if (ret != ISC_R_SUCCESS)
+ goto err;
+
+ bits = key->key_size;
+ PK11_RET(pkcs_C_GenerateKey,
+ (pk11_ctx->session, &mech, dpTemplate, (CK_ULONG) 5, &dp),
+ DST_R_CRYPTOFAILURE);
+
+ dsa = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*dsa));
+ if (dsa == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(dsa, 0, sizeof(*dsa));
+ key->keydata.pkey = dsa;
+ dsa->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 5);
+ if (dsa->repr == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(dsa->repr, 0, sizeof(*attr) * 5);
+ dsa->attrcnt = 5;
+
+ attr = dsa->repr;
+ attr[0].type = CKA_PRIME;
+ attr[1].type = CKA_SUBPRIME;
+ attr[2].type = CKA_BASE;
+ attr[3].type = CKA_VALUE;
+ attr[4].type = CKA_VALUE2;
+
+ PK11_RET(pkcs_C_GetAttributeValue,
+ (pk11_ctx->session, dp, attr, 3),
+ DST_R_CRYPTOFAILURE);
+
+ for (i = 0; i <= 2; i++) {
+ attr[i].pValue = isc_mem_get(key->mctx, attr[i].ulValueLen);
+ if (attr[i].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(attr[i].pValue, 0, attr[i].ulValueLen);
+ }
+ PK11_RET(pkcs_C_GetAttributeValue,
+ (pk11_ctx->session, dp, attr, 3),
+ DST_R_CRYPTOFAILURE);
+ pubTemplate[5].pValue = attr[0].pValue;
+ pubTemplate[5].ulValueLen = attr[0].ulValueLen;
+ pubTemplate[6].pValue = attr[1].pValue;
+ pubTemplate[6].ulValueLen = attr[1].ulValueLen;
+ pubTemplate[7].pValue = attr[2].pValue;
+ pubTemplate[7].ulValueLen = attr[2].ulValueLen;
+
+ mech.mechanism = CKM_DSA_KEY_PAIR_GEN;
+ PK11_RET(pkcs_C_GenerateKeyPair,
+ (pk11_ctx->session, &mech,
+ pubTemplate, (CK_ULONG) 8,
+ privTemplate, (CK_ULONG) 7,
+ &pub, &priv),
+ DST_R_CRYPTOFAILURE);
+
+ attr = dsa->repr;
+ attr += 3;
+ PK11_RET(pkcs_C_GetAttributeValue,
+ (pk11_ctx->session, pub, attr, 1),
+ DST_R_CRYPTOFAILURE);
+ attr->pValue = isc_mem_get(key->mctx, attr->ulValueLen);
+ if (attr->pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(attr->pValue, 0, attr->ulValueLen);
+ PK11_RET(pkcs_C_GetAttributeValue,
+ (pk11_ctx->session, pub, attr, 1),
+ DST_R_CRYPTOFAILURE);
+
+ attr++;
+ attr->type = CKA_VALUE;
+ PK11_RET(pkcs_C_GetAttributeValue,
+ (pk11_ctx->session, priv, attr, 1),
+ DST_R_CRYPTOFAILURE);
+ attr->pValue = isc_mem_get(key->mctx, attr->ulValueLen);
+ if (attr->pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(attr->pValue, 0, attr->ulValueLen);
+ PK11_RET(pkcs_C_GetAttributeValue,
+ (pk11_ctx->session, priv, attr, 1),
+ DST_R_CRYPTOFAILURE);
+ attr->type = CKA_VALUE2;
+
+ (void) pkcs_C_DestroyObject(pk11_ctx->session, priv);
+ (void) pkcs_C_DestroyObject(pk11_ctx->session, pub);
+ (void) pkcs_C_DestroyObject(pk11_ctx->session, dp);
+ pk11_return_session(pk11_ctx);
+ isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx));
+ isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+ return (ISC_R_SUCCESS);
+
+ err:
+ pkcs11dsa_destroy(key);
+ if (priv != CK_INVALID_HANDLE)
+ (void) pkcs_C_DestroyObject(pk11_ctx->session, priv);
+ if (pub != CK_INVALID_HANDLE)
+ (void) pkcs_C_DestroyObject(pk11_ctx->session, pub);
+ if (dp != CK_INVALID_HANDLE)
+ (void) pkcs_C_DestroyObject(pk11_ctx->session, dp);
+ pk11_return_session(pk11_ctx);
+ isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx));
+ isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+ return (ret);
+}
+
+static bool
+pkcs11dsa_isprivate(const dst_key_t *key) {
+ pk11_object_t *dsa = key->keydata.pkey;
+ CK_ATTRIBUTE *attr;
+
+ if (dsa == NULL)
+ return (false);
+ attr = pk11_attribute_bytype(dsa, CKA_VALUE2);
+ return (attr != NULL || dsa->ontoken);
+}
+
+static void
+pkcs11dsa_destroy(dst_key_t *key) {
+ pk11_object_t *dsa = key->keydata.pkey;
+ CK_ATTRIBUTE *attr;
+
+ if (dsa == NULL)
+ return;
+
+ INSIST((dsa->object == CK_INVALID_HANDLE) || dsa->ontoken);
+
+ for (attr = pk11_attribute_first(dsa);
+ attr != NULL;
+ attr = pk11_attribute_next(dsa, attr))
+ switch (attr->type) {
+ case CKA_PRIME:
+ case CKA_SUBPRIME:
+ case CKA_BASE:
+ case CKA_VALUE:
+ case CKA_VALUE2:
+ if (attr->pValue != NULL) {
+ isc_safe_memwipe(attr->pValue,
+ attr->ulValueLen);
+ isc_mem_put(key->mctx,
+ attr->pValue,
+ attr->ulValueLen);
+ }
+ break;
+ }
+ if (dsa->repr != NULL) {
+ isc_safe_memwipe(dsa->repr, dsa->attrcnt * sizeof(*attr));
+ isc_mem_put(key->mctx,
+ dsa->repr,
+ dsa->attrcnt * sizeof(*attr));
+ }
+ isc_safe_memwipe(dsa, sizeof(*dsa));
+ isc_mem_put(key->mctx, dsa, sizeof(*dsa));
+ key->keydata.pkey = NULL;
+}
+
+
+static isc_result_t
+pkcs11dsa_todns(const dst_key_t *key, isc_buffer_t *data) {
+ pk11_object_t *dsa;
+ CK_ATTRIBUTE *attr;
+ isc_region_t r;
+ int dnslen;
+ unsigned int t, p_bytes;
+ CK_ATTRIBUTE *prime = NULL, *subprime = NULL;
+ CK_ATTRIBUTE *base = NULL, *pub_key = NULL;
+ CK_BYTE *cp;
+
+ REQUIRE(key->keydata.pkey != NULL);
+
+ dsa = key->keydata.pkey;
+
+ for (attr = pk11_attribute_first(dsa);
+ attr != NULL;
+ attr = pk11_attribute_next(dsa, attr))
+ switch (attr->type) {
+ case CKA_PRIME:
+ prime = attr;
+ break;
+ case CKA_SUBPRIME:
+ subprime = attr;
+ break;
+ case CKA_BASE:
+ base = attr;
+ break;
+ case CKA_VALUE:
+ pub_key = attr;
+ break;
+ }
+ REQUIRE((prime != NULL) && (subprime != NULL) &&
+ (base != NULL) && (pub_key != NULL));
+
+ isc_buffer_availableregion(data, &r);
+
+ t = (prime->ulValueLen - 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);
+
+ memset(r.base, 0, dnslen);
+ *r.base = t;
+ isc_region_consume(&r, 1);
+
+ cp = (CK_BYTE *) subprime->pValue;
+ memmove(r.base + ISC_SHA1_DIGESTLENGTH - subprime->ulValueLen,
+ cp, subprime->ulValueLen);
+ isc_region_consume(&r, ISC_SHA1_DIGESTLENGTH);
+ cp = (CK_BYTE *) prime->pValue;
+ memmove(r.base + key->key_size/8 - prime->ulValueLen,
+ cp, prime->ulValueLen);
+ isc_region_consume(&r, p_bytes);
+ cp = (CK_BYTE *) base->pValue;
+ memmove(r.base + key->key_size/8 - base->ulValueLen,
+ cp, base->ulValueLen);
+ isc_region_consume(&r, p_bytes);
+ cp = (CK_BYTE *) pub_key->pValue;
+ memmove(r.base + key->key_size/8 - pub_key->ulValueLen,
+ cp, pub_key->ulValueLen);
+ isc_region_consume(&r, p_bytes);
+
+ isc_buffer_add(data, dnslen);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+pkcs11dsa_fromdns(dst_key_t *key, isc_buffer_t *data) {
+ pk11_object_t *dsa;
+ isc_region_t r;
+ unsigned int t, p_bytes;
+ CK_BYTE *prime, *subprime, *base, *pub_key;
+ CK_ATTRIBUTE *attr;
+
+ isc_buffer_remainingregion(data, &r);
+ if (r.length == 0)
+ return (ISC_R_SUCCESS);
+
+ dsa = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*dsa));
+ if (dsa == NULL)
+ return (ISC_R_NOMEMORY);
+ memset(dsa, 0, sizeof(*dsa));
+
+ t = (unsigned int) *r.base;
+ isc_region_consume(&r, 1);
+ if (t > 8) {
+ isc_safe_memwipe(dsa, sizeof(*dsa));
+ isc_mem_put(key->mctx, dsa, sizeof(*dsa));
+ return (DST_R_INVALIDPUBLICKEY);
+ }
+ p_bytes = 64 + 8 * t;
+
+ if (r.length < ISC_SHA1_DIGESTLENGTH + 3 * p_bytes) {
+ isc_safe_memwipe(dsa, sizeof(*dsa));
+ isc_mem_put(key->mctx, dsa, sizeof(*dsa));
+ return (DST_R_INVALIDPUBLICKEY);
+ }
+
+ subprime = r.base;
+ isc_region_consume(&r, ISC_SHA1_DIGESTLENGTH);
+
+ prime = r.base;
+ isc_region_consume(&r, p_bytes);
+
+ base = r.base;
+ isc_region_consume(&r, p_bytes);
+
+ pub_key = r.base;
+ isc_region_consume(&r, p_bytes);
+
+ key->key_size = p_bytes * 8;
+
+ isc_buffer_forward(data, 1 + ISC_SHA1_DIGESTLENGTH + 3 * p_bytes);
+
+ dsa->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 4);
+ if (dsa->repr == NULL)
+ goto nomemory;
+ memset(dsa->repr, 0, sizeof(*attr) * 4);
+ dsa->attrcnt = 4;
+
+ attr = dsa->repr;
+ attr[0].type = CKA_PRIME;
+ attr[0].pValue = isc_mem_get(key->mctx, p_bytes);
+ if (attr[0].pValue == NULL)
+ goto nomemory;
+ memmove(attr[0].pValue, prime, p_bytes);
+ attr[0].ulValueLen = p_bytes;
+
+ attr[1].type = CKA_SUBPRIME;
+ attr[1].pValue = isc_mem_get(key->mctx, ISC_SHA1_DIGESTLENGTH);
+ if (attr[1].pValue == NULL)
+ goto nomemory;
+ memmove(attr[1].pValue, subprime, ISC_SHA1_DIGESTLENGTH);
+ attr[1].ulValueLen = ISC_SHA1_DIGESTLENGTH;
+
+ attr[2].type = CKA_BASE;
+ attr[2].pValue = isc_mem_get(key->mctx, p_bytes);
+ if (attr[2].pValue == NULL)
+ goto nomemory;
+ memmove(attr[2].pValue, base, p_bytes);
+ attr[2].ulValueLen = p_bytes;
+
+ attr[3].type = CKA_VALUE;
+ attr[3].pValue = isc_mem_get(key->mctx, p_bytes);
+ if (attr[3].pValue == NULL)
+ goto nomemory;
+ memmove(attr[3].pValue, pub_key, p_bytes);
+ attr[3].ulValueLen = p_bytes;
+
+ key->keydata.pkey = dsa;
+
+ return (ISC_R_SUCCESS);
+
+ nomemory:
+ for (attr = pk11_attribute_first(dsa);
+ attr != NULL;
+ attr = pk11_attribute_next(dsa, attr))
+ switch (attr->type) {
+ case CKA_PRIME:
+ case CKA_SUBPRIME:
+ case CKA_BASE:
+ case CKA_VALUE:
+ if (attr->pValue != NULL) {
+ isc_safe_memwipe(attr->pValue,
+ attr->ulValueLen);
+ isc_mem_put(key->mctx,
+ attr->pValue,
+ attr->ulValueLen);
+ }
+ break;
+ }
+ if (dsa->repr != NULL) {
+ isc_safe_memwipe(dsa->repr, dsa->attrcnt * sizeof(*attr));
+ isc_mem_put(key->mctx,
+ dsa->repr,
+ dsa->attrcnt * sizeof(*attr));
+ }
+ isc_safe_memwipe(dsa, sizeof(*dsa));
+ isc_mem_put(key->mctx, dsa, sizeof(*dsa));
+ return (ISC_R_NOMEMORY);
+}
+
+static isc_result_t
+pkcs11dsa_tofile(const dst_key_t *key, const char *directory) {
+ int cnt = 0;
+ pk11_object_t *dsa;
+ CK_ATTRIBUTE *attr;
+ CK_ATTRIBUTE *prime = NULL, *subprime = NULL, *base = NULL;
+ CK_ATTRIBUTE *pub_key = NULL, *priv_key = NULL;
+ dst_private_t priv;
+ unsigned char bufs[5][128];
+
+ if (key->keydata.pkey == NULL)
+ return (DST_R_NULLKEY);
+
+ if (key->external) {
+ priv.nelements = 0;
+ return (dst__privstruct_writefile(key, &priv, directory));
+ }
+
+ dsa = key->keydata.pkey;
+
+ for (attr = pk11_attribute_first(dsa);
+ attr != NULL;
+ attr = pk11_attribute_next(dsa, attr))
+ switch (attr->type) {
+ case CKA_PRIME:
+ prime = attr;
+ break;
+ case CKA_SUBPRIME:
+ subprime = attr;
+ break;
+ case CKA_BASE:
+ base = attr;
+ break;
+ case CKA_VALUE:
+ pub_key = attr;
+ break;
+ case CKA_VALUE2:
+ priv_key = attr;
+ break;
+ }
+ if ((prime == NULL) || (subprime == NULL) || (base == NULL) ||
+ (pub_key == NULL) || (priv_key ==NULL))
+ return (DST_R_NULLKEY);
+
+ priv.elements[cnt].tag = TAG_DSA_PRIME;
+ priv.elements[cnt].length = (unsigned short) prime->ulValueLen;
+ memmove(bufs[cnt], prime->pValue, prime->ulValueLen);
+ priv.elements[cnt].data = bufs[cnt];
+ cnt++;
+
+ priv.elements[cnt].tag = TAG_DSA_SUBPRIME;
+ priv.elements[cnt].length = (unsigned short) subprime->ulValueLen;
+ memmove(bufs[cnt], subprime->pValue, subprime->ulValueLen);
+ priv.elements[cnt].data = bufs[cnt];
+ cnt++;
+
+ priv.elements[cnt].tag = TAG_DSA_BASE;
+ priv.elements[cnt].length = (unsigned short) base->ulValueLen;
+ memmove(bufs[cnt], base->pValue, base->ulValueLen);
+ priv.elements[cnt].data = bufs[cnt];
+ cnt++;
+
+ priv.elements[cnt].tag = TAG_DSA_PRIVATE;
+ priv.elements[cnt].length = (unsigned short) priv_key->ulValueLen;
+ memmove(bufs[cnt], priv_key->pValue, priv_key->ulValueLen);
+ priv.elements[cnt].data = bufs[cnt];
+ cnt++;
+
+ priv.elements[cnt].tag = TAG_DSA_PUBLIC;
+ priv.elements[cnt].length = (unsigned short) pub_key->ulValueLen;
+ memmove(bufs[cnt], pub_key->pValue, pub_key->ulValueLen);
+ priv.elements[cnt].data = bufs[cnt];
+ cnt++;
+
+ priv.nelements = cnt;
+ return (dst__privstruct_writefile(key, &priv, directory));
+}
+
+static isc_result_t
+pkcs11dsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
+ dst_private_t priv;
+ isc_result_t ret;
+ int i;
+ pk11_object_t *dsa = NULL;
+ CK_ATTRIBUTE *attr;
+ isc_mem_t *mctx = key->mctx;
+
+ /* read private key file */
+ ret = dst__privstruct_parse(key, DST_ALG_DSA, lexer, mctx, &priv);
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+
+ if (key->external) {
+ if (priv.nelements != 0)
+ DST_RET(DST_R_INVALIDPRIVATEKEY);
+ if (pub == NULL)
+ DST_RET(DST_R_INVALIDPRIVATEKEY);
+
+ key->keydata.pkey = pub->keydata.pkey;
+ pub->keydata.pkey = NULL;
+ key->key_size = pub->key_size;
+
+ dst__privstruct_free(&priv, mctx);
+ isc_safe_memwipe(&priv, sizeof(priv));
+
+ return (ISC_R_SUCCESS);
+ }
+
+ dsa = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*dsa));
+ if (dsa == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(dsa, 0, sizeof(*dsa));
+ key->keydata.pkey = dsa;
+
+ dsa->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 5);
+ if (dsa->repr == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(dsa->repr, 0, sizeof(*attr) * 5);
+ dsa->attrcnt = 5;
+ attr = dsa->repr;
+ attr[0].type = CKA_PRIME;
+ attr[1].type = CKA_SUBPRIME;
+ attr[2].type = CKA_BASE;
+ attr[3].type = CKA_VALUE;
+ attr[4].type = CKA_VALUE2;
+
+ for (i = 0; i < priv.nelements; i++) {
+ CK_BYTE *bn;
+
+ bn = isc_mem_get(key->mctx, priv.elements[i].length);
+ if (bn == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(bn, priv.elements[i].data, priv.elements[i].length);
+
+ switch (priv.elements[i].tag) {
+ case TAG_DSA_PRIME:
+ attr = pk11_attribute_bytype(dsa, CKA_PRIME);
+ INSIST(attr != NULL);
+ attr->pValue = bn;
+ attr->ulValueLen = priv.elements[i].length;
+ break;
+ case TAG_DSA_SUBPRIME:
+ attr = pk11_attribute_bytype(dsa,
+ CKA_SUBPRIME);
+ INSIST(attr != NULL);
+ attr->pValue = bn;
+ attr->ulValueLen = priv.elements[i].length;
+ break;
+ case TAG_DSA_BASE:
+ attr = pk11_attribute_bytype(dsa, CKA_BASE);
+ INSIST(attr != NULL);
+ attr->pValue = bn;
+ attr->ulValueLen = priv.elements[i].length;
+ break;
+ case TAG_DSA_PRIVATE:
+ attr = pk11_attribute_bytype(dsa, CKA_VALUE2);
+ INSIST(attr != NULL);
+ attr->pValue = bn;
+ attr->ulValueLen = priv.elements[i].length;
+ break;
+ case TAG_DSA_PUBLIC:
+ attr = pk11_attribute_bytype(dsa, CKA_VALUE);
+ INSIST(attr != NULL);
+ attr->pValue = bn;
+ attr->ulValueLen = priv.elements[i].length;
+ break;
+ }
+ }
+ dst__privstruct_free(&priv, mctx);
+
+ attr = pk11_attribute_bytype(dsa, CKA_PRIME);
+ INSIST(attr != NULL);
+ key->key_size = pk11_numbits(attr->pValue, attr->ulValueLen);
+
+ return (ISC_R_SUCCESS);
+
+ err:
+ pkcs11dsa_destroy(key);
+ dst__privstruct_free(&priv, mctx);
+ isc_safe_memwipe(&priv, sizeof(priv));
+ return (ret);
+}
+
+static dst_func_t pkcs11dsa_functions = {
+ pkcs11dsa_createctx,
+ NULL, /*%< createctx2 */
+ pkcs11dsa_destroyctx,
+ pkcs11dsa_adddata,
+ pkcs11dsa_sign,
+ pkcs11dsa_verify,
+ NULL, /*%< verify2 */
+ NULL, /*%< computesecret */
+ pkcs11dsa_compare,
+ NULL, /*%< paramcompare */
+ pkcs11dsa_generate,
+ pkcs11dsa_isprivate,
+ pkcs11dsa_destroy,
+ pkcs11dsa_todns,
+ pkcs11dsa_fromdns,
+ pkcs11dsa_tofile,
+ pkcs11dsa_parse,
+ NULL, /*%< cleanup */
+ NULL, /*%< fromlabel */
+ NULL, /*%< dump */
+ NULL, /*%< restore */
+};
+
+isc_result_t
+dst__pkcs11dsa_init(dst_func_t **funcp) {
+ REQUIRE(funcp != NULL);
+ if (*funcp == NULL)
+ *funcp = &pkcs11dsa_functions;
+ return (ISC_R_SUCCESS);
+}
+#endif /* !PK11_DSA_DISABLE */
+
+#else /* PKCS11CRYPTO */
+
+#include <isc/util.h>
+
+EMPTY_TRANSLATION_UNIT
+
+#endif /* PKCS11CRYPTO */
+/*! \file */
diff --git a/lib/dns/pkcs11ecdsa_link.c b/lib/dns/pkcs11ecdsa_link.c
new file mode 100644
index 0000000..4a39cc4
--- /dev/null
+++ b/lib/dns/pkcs11ecdsa_link.c
@@ -0,0 +1,1198 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <config.h>
+
+#if defined(PKCS11CRYPTO) && defined(HAVE_PKCS11_ECDSA)
+
+#include <stdbool.h>
+
+#include <isc/entropy.h>
+#include <isc/mem.h>
+#include <isc/safe.h>
+#include <isc/sha2.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dns/keyvalues.h>
+#include <dst/result.h>
+
+#include "dst_internal.h"
+#include "dst_parse.h"
+#include "dst_pkcs11.h"
+
+#include <pk11/pk11.h>
+#include <pk11/internal.h>
+#define WANT_ECC_CURVES
+#include <pk11/constants.h>
+
+#include <pkcs11/pkcs11.h>
+
+/*
+ * FIPS 186-3 ECDSA keys:
+ * mechanisms:
+ * CKM_ECDSA,
+ * CKM_EC_KEY_PAIR_GEN
+ * domain parameters:
+ * CKA_EC_PARAMS (choice with OID namedCurve)
+ * public keys:
+ * object class CKO_PUBLIC_KEY
+ * key type CKK_EC
+ * attribute CKA_EC_PARAMS (choice with OID namedCurve)
+ * attribute CKA_EC_POINT (point Q)
+ * private keys:
+ * object class CKO_PRIVATE_KEY
+ * key type CKK_EC
+ * attribute CKA_EC_PARAMS (choice with OID namedCurve)
+ * attribute CKA_VALUE (big int d)
+ * point format: 0x04 (octet-string) <2*size+1> 0x4 (uncompressed) <x> <y>
+ */
+
+#define TAG_OCTECT_STRING 0x04
+#define UNCOMPRESSED 0x04
+
+#define DST_RET(a) {ret = a; goto err;}
+
+static CK_BBOOL truevalue = TRUE;
+static CK_BBOOL falsevalue = FALSE;
+
+static isc_result_t pkcs11ecdsa_todns(const dst_key_t *key,
+ isc_buffer_t *data);
+static void pkcs11ecdsa_destroy(dst_key_t *key);
+static isc_result_t pkcs11ecdsa_fetch(dst_key_t *key, const char *engine,
+ const char *label, dst_key_t *pub);
+
+static isc_result_t
+pkcs11ecdsa_createctx(dst_key_t *key, dst_context_t *dctx) {
+ CK_RV rv;
+ CK_MECHANISM mech = {0, NULL, 0 };
+ CK_SLOT_ID slotid;
+ pk11_context_t *pk11_ctx;
+ pk11_object_t *ec = key->keydata.pkey;
+ isc_result_t ret;
+
+ REQUIRE(dctx->key->key_alg == DST_ALG_ECDSA256 ||
+ dctx->key->key_alg == DST_ALG_ECDSA384);
+ REQUIRE(ec != NULL);
+
+ if (dctx->key->key_alg == DST_ALG_ECDSA256)
+ mech.mechanism = CKM_SHA256;
+ else
+ mech.mechanism = CKM_SHA384;
+
+ pk11_ctx = (pk11_context_t *) isc_mem_get(dctx->mctx,
+ sizeof(*pk11_ctx));
+ if (pk11_ctx == NULL)
+ return (ISC_R_NOMEMORY);
+ memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+ if (ec->ontoken && (dctx->use == DO_SIGN))
+ slotid = ec->slot;
+ else
+ slotid = pk11_get_best_token(OP_EC);
+ ret = pk11_get_session(pk11_ctx, OP_EC, true, false,
+ ec->reqlogon, NULL, slotid);
+ if (ret != ISC_R_SUCCESS)
+ goto err;
+
+ PK11_RET(pkcs_C_DigestInit, (pk11_ctx->session, &mech), ISC_R_FAILURE);
+ dctx->ctxdata.pk11_ctx = pk11_ctx;
+ return (ISC_R_SUCCESS);
+
+ err:
+ pk11_return_session(pk11_ctx);
+ memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+ isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+ return (ret);
+}
+
+static void
+pkcs11ecdsa_destroyctx(dst_context_t *dctx) {
+ CK_BYTE garbage[ISC_SHA384_DIGESTLENGTH];
+ CK_ULONG len = ISC_SHA384_DIGESTLENGTH;
+ pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx;
+
+ REQUIRE(dctx->key->key_alg == DST_ALG_ECDSA256 ||
+ dctx->key->key_alg == DST_ALG_ECDSA384);
+
+ if (pk11_ctx != NULL) {
+ (void) pkcs_C_DigestFinal(pk11_ctx->session, garbage, &len);
+ memset(garbage, 0, sizeof(garbage));
+ pk11_return_session(pk11_ctx);
+ memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+ isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx));
+ dctx->ctxdata.pk11_ctx = NULL;
+ }
+}
+
+static isc_result_t
+pkcs11ecdsa_adddata(dst_context_t *dctx, const isc_region_t *data) {
+ CK_RV rv;
+ pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx;
+ isc_result_t ret = ISC_R_SUCCESS;
+
+ REQUIRE(dctx->key->key_alg == DST_ALG_ECDSA256 ||
+ dctx->key->key_alg == DST_ALG_ECDSA384);
+
+ PK11_CALL(pkcs_C_DigestUpdate,
+ (pk11_ctx->session,
+ (CK_BYTE_PTR) data->base,
+ (CK_ULONG) data->length),
+ ISC_R_FAILURE);
+
+ return (ret);
+}
+
+static isc_result_t
+pkcs11ecdsa_sign(dst_context_t *dctx, isc_buffer_t *sig) {
+ CK_RV rv;
+ CK_MECHANISM mech = { CKM_ECDSA, NULL, 0 };
+ CK_OBJECT_HANDLE hKey = CK_INVALID_HANDLE;
+ CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY;
+ CK_KEY_TYPE keyType = CKK_EC;
+ CK_ATTRIBUTE keyTemplate[] =
+ {
+ { CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+ { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+ { CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_SIGN, &truevalue, (CK_ULONG) sizeof(truevalue) },
+ { CKA_EC_PARAMS, NULL, 0 },
+ { CKA_VALUE, NULL, 0 }
+ };
+ CK_ATTRIBUTE *attr;
+ CK_BYTE digest[ISC_SHA384_DIGESTLENGTH];
+ CK_ULONG dgstlen;
+ CK_ULONG siglen;
+ pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx;
+ dst_key_t *key = dctx->key;
+ pk11_object_t *ec = key->keydata.pkey;
+ isc_region_t r;
+ isc_result_t ret = ISC_R_SUCCESS;
+ unsigned int i;
+
+ REQUIRE(key->key_alg == DST_ALG_ECDSA256 ||
+ key->key_alg == DST_ALG_ECDSA384);
+ REQUIRE(ec != NULL);
+
+ if (key->key_alg == DST_ALG_ECDSA256) {
+ dgstlen = ISC_SHA256_DIGESTLENGTH;
+ siglen = DNS_SIG_ECDSA256SIZE;
+ } else {
+ siglen = DNS_SIG_ECDSA384SIZE;
+ dgstlen = ISC_SHA384_DIGESTLENGTH;
+ }
+
+ PK11_RET(pkcs_C_DigestFinal,
+ (pk11_ctx->session, digest, &dgstlen),
+ ISC_R_FAILURE);
+
+ isc_buffer_availableregion(sig, &r);
+ if (r.length < siglen)
+ DST_RET(ISC_R_NOSPACE);
+
+ if (ec->ontoken && (ec->object != CK_INVALID_HANDLE)) {
+ pk11_ctx->ontoken = ec->ontoken;
+ pk11_ctx->object = ec->object;
+ goto token_key;
+ }
+
+ for (attr = pk11_attribute_first(ec);
+ attr != NULL;
+ attr = pk11_attribute_next(ec, attr))
+ switch (attr->type) {
+ case CKA_EC_PARAMS:
+ INSIST(keyTemplate[5].type == attr->type);
+ keyTemplate[5].pValue = isc_mem_get(dctx->mctx,
+ attr->ulValueLen);
+ if (keyTemplate[5].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[5].pValue, attr->pValue,
+ attr->ulValueLen);
+ keyTemplate[5].ulValueLen = attr->ulValueLen;
+ break;
+ case CKA_VALUE:
+ INSIST(keyTemplate[6].type == attr->type);
+ keyTemplate[6].pValue = isc_mem_get(dctx->mctx,
+ attr->ulValueLen);
+ if (keyTemplate[6].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[6].pValue, attr->pValue,
+ attr->ulValueLen);
+ keyTemplate[6].ulValueLen = attr->ulValueLen;
+ break;
+ }
+ pk11_ctx->object = CK_INVALID_HANDLE;
+ pk11_ctx->ontoken = false;
+ PK11_RET(pkcs_C_CreateObject,
+ (pk11_ctx->session,
+ keyTemplate, (CK_ULONG) 7,
+ &hKey),
+ ISC_R_FAILURE);
+
+ token_key:
+
+ PK11_RET(pkcs_C_SignInit,
+ (pk11_ctx->session, &mech,
+ pk11_ctx->ontoken ? pk11_ctx->object : hKey),
+ ISC_R_FAILURE);
+
+ PK11_RET(pkcs_C_Sign,
+ (pk11_ctx->session,
+ digest, dgstlen,
+ (CK_BYTE_PTR) r.base, &siglen),
+ DST_R_SIGNFAILURE);
+
+ isc_buffer_add(sig, (unsigned int) siglen);
+
+ err:
+
+ if (hKey != CK_INVALID_HANDLE)
+ (void) pkcs_C_DestroyObject(pk11_ctx->session, hKey);
+ for (i = 5; i <= 6; i++)
+ if (keyTemplate[i].pValue != NULL) {
+ memset(keyTemplate[i].pValue, 0,
+ keyTemplate[i].ulValueLen);
+ isc_mem_put(dctx->mctx,
+ keyTemplate[i].pValue,
+ keyTemplate[i].ulValueLen);
+ }
+ pk11_return_session(pk11_ctx);
+ memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+ isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx));
+ dctx->ctxdata.pk11_ctx = NULL;
+
+ return (ret);
+}
+
+static isc_result_t
+pkcs11ecdsa_verify(dst_context_t *dctx, const isc_region_t *sig) {
+ CK_RV rv;
+ CK_MECHANISM mech = { CKM_ECDSA, NULL, 0 };
+ CK_OBJECT_HANDLE hKey = CK_INVALID_HANDLE;
+ CK_OBJECT_CLASS keyClass = CKO_PUBLIC_KEY;
+ CK_KEY_TYPE keyType = CKK_EC;
+ CK_ATTRIBUTE keyTemplate[] =
+ {
+ { CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+ { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+ { CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_VERIFY, &truevalue, (CK_ULONG) sizeof(truevalue) },
+ { CKA_EC_PARAMS, NULL, 0 },
+ { CKA_EC_POINT, NULL, 0 }
+ };
+ CK_ATTRIBUTE *attr;
+ CK_BYTE digest[ISC_SHA384_DIGESTLENGTH];
+ CK_ULONG dgstlen;
+ pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx;
+ dst_key_t *key = dctx->key;
+ pk11_object_t *ec = key->keydata.pkey;
+ isc_result_t ret = ISC_R_SUCCESS;
+ unsigned int i;
+
+ REQUIRE(key->key_alg == DST_ALG_ECDSA256 ||
+ key->key_alg == DST_ALG_ECDSA384);
+ REQUIRE(ec != NULL);
+
+ if (key->key_alg == DST_ALG_ECDSA256)
+ dgstlen = ISC_SHA256_DIGESTLENGTH;
+ else
+ dgstlen = ISC_SHA384_DIGESTLENGTH;
+
+ PK11_RET(pkcs_C_DigestFinal,
+ (pk11_ctx->session, digest, &dgstlen),
+ ISC_R_FAILURE);
+
+ for (attr = pk11_attribute_first(ec);
+ attr != NULL;
+ attr = pk11_attribute_next(ec, attr))
+ switch (attr->type) {
+ case CKA_EC_PARAMS:
+ INSIST(keyTemplate[5].type == attr->type);
+ keyTemplate[5].pValue = isc_mem_get(dctx->mctx,
+ attr->ulValueLen);
+ if (keyTemplate[5].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[5].pValue, attr->pValue,
+ attr->ulValueLen);
+ keyTemplate[5].ulValueLen = attr->ulValueLen;
+ break;
+ case CKA_EC_POINT:
+ INSIST(keyTemplate[6].type == attr->type);
+ keyTemplate[6].pValue = isc_mem_get(dctx->mctx,
+ attr->ulValueLen);
+ if (keyTemplate[6].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[6].pValue, attr->pValue,
+ attr->ulValueLen);
+ keyTemplate[6].ulValueLen = attr->ulValueLen;
+ break;
+ }
+ pk11_ctx->object = CK_INVALID_HANDLE;
+ pk11_ctx->ontoken = false;
+ PK11_RET(pkcs_C_CreateObject,
+ (pk11_ctx->session,
+ keyTemplate, (CK_ULONG) 7,
+ &hKey),
+ ISC_R_FAILURE);
+
+ PK11_RET(pkcs_C_VerifyInit,
+ (pk11_ctx->session, &mech, hKey),
+ ISC_R_FAILURE);
+
+ PK11_RET(pkcs_C_Verify,
+ (pk11_ctx->session,
+ digest, dgstlen,
+ (CK_BYTE_PTR) sig->base, (CK_ULONG) sig->length),
+ DST_R_VERIFYFAILURE);
+
+ err:
+
+ if (hKey != CK_INVALID_HANDLE)
+ (void) pkcs_C_DestroyObject(pk11_ctx->session, hKey);
+ for (i = 5; i <= 6; i++)
+ if (keyTemplate[i].pValue != NULL) {
+ memset(keyTemplate[i].pValue, 0,
+ keyTemplate[i].ulValueLen);
+ isc_mem_put(dctx->mctx,
+ keyTemplate[i].pValue,
+ keyTemplate[i].ulValueLen);
+ }
+ pk11_return_session(pk11_ctx);
+ memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+ isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx));
+ dctx->ctxdata.pk11_ctx = NULL;
+
+ return (ret);
+}
+
+static bool
+pkcs11ecdsa_compare(const dst_key_t *key1, const dst_key_t *key2) {
+ pk11_object_t *ec1, *ec2;
+ CK_ATTRIBUTE *attr1, *attr2;
+
+ ec1 = key1->keydata.pkey;
+ ec2 = key2->keydata.pkey;
+
+ if ((ec1 == NULL) && (ec2 == NULL))
+ return (true);
+ else if ((ec1 == NULL) || (ec2 == NULL))
+ return (false);
+
+ attr1 = pk11_attribute_bytype(ec1, CKA_EC_PARAMS);
+ attr2 = pk11_attribute_bytype(ec2, CKA_EC_PARAMS);
+ if ((attr1 == NULL) && (attr2 == NULL))
+ return (true);
+ else if ((attr1 == NULL) || (attr2 == NULL) ||
+ (attr1->ulValueLen != attr2->ulValueLen) ||
+ !isc_safe_memequal(attr1->pValue, attr2->pValue,
+ attr1->ulValueLen))
+ return (false);
+
+ attr1 = pk11_attribute_bytype(ec1, CKA_EC_POINT);
+ attr2 = pk11_attribute_bytype(ec2, CKA_EC_POINT);
+ if ((attr1 == NULL) && (attr2 == NULL))
+ return (true);
+ else if ((attr1 == NULL) || (attr2 == NULL) ||
+ (attr1->ulValueLen != attr2->ulValueLen) ||
+ !isc_safe_memequal(attr1->pValue, attr2->pValue,
+ attr1->ulValueLen))
+ return (false);
+
+ attr1 = pk11_attribute_bytype(ec1, CKA_VALUE);
+ attr2 = pk11_attribute_bytype(ec2, CKA_VALUE);
+ if (((attr1 != NULL) || (attr2 != NULL)) &&
+ ((attr1 == NULL) || (attr2 == NULL) ||
+ (attr1->ulValueLen != attr2->ulValueLen) ||
+ !isc_safe_memequal(attr1->pValue, attr2->pValue,
+ attr1->ulValueLen)))
+ return (false);
+
+ if (!ec1->ontoken && !ec2->ontoken)
+ return (true);
+ else if (ec1->ontoken || ec2->ontoken ||
+ (ec1->object != ec2->object))
+ return (false);
+
+ return (true);
+}
+
+#define SETCURVE() \
+ if (key->key_alg == DST_ALG_ECDSA256) { \
+ attr->pValue = isc_mem_get(key->mctx, \
+ sizeof(pk11_ecc_prime256v1)); \
+ if (attr->pValue == NULL) \
+ DST_RET(ISC_R_NOMEMORY); \
+ memmove(attr->pValue, \
+ pk11_ecc_prime256v1, sizeof(pk11_ecc_prime256v1)); \
+ attr->ulValueLen = sizeof(pk11_ecc_prime256v1); \
+ } else { \
+ attr->pValue = isc_mem_get(key->mctx, \
+ sizeof(pk11_ecc_secp384r1)); \
+ if (attr->pValue == NULL) \
+ DST_RET(ISC_R_NOMEMORY); \
+ memmove(attr->pValue, \
+ pk11_ecc_secp384r1, sizeof(pk11_ecc_secp384r1)); \
+ attr->ulValueLen = sizeof(pk11_ecc_secp384r1); \
+ }
+
+#define FREECURVE() \
+ if (attr->pValue != NULL) { \
+ memset(attr->pValue, 0, attr->ulValueLen); \
+ isc_mem_put(key->mctx, attr->pValue, attr->ulValueLen); \
+ attr->pValue = NULL; \
+ }
+
+static isc_result_t
+pkcs11ecdsa_generate(dst_key_t *key, int unused, void (*callback)(int)) {
+ CK_RV rv;
+ CK_MECHANISM mech = { CKM_EC_KEY_PAIR_GEN, NULL, 0 };
+ CK_OBJECT_HANDLE pub = CK_INVALID_HANDLE;
+ CK_OBJECT_CLASS pubClass = CKO_PUBLIC_KEY;
+ CK_KEY_TYPE keyType = CKK_EC;
+ CK_ATTRIBUTE pubTemplate[] =
+ {
+ { CKA_CLASS, &pubClass, (CK_ULONG) sizeof(pubClass) },
+ { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+ { CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_VERIFY, &truevalue, (CK_ULONG) sizeof(truevalue) },
+ { CKA_EC_PARAMS, NULL, 0 }
+ };
+ CK_OBJECT_HANDLE priv = CK_INVALID_HANDLE;
+ CK_OBJECT_HANDLE privClass = CKO_PRIVATE_KEY;
+ CK_ATTRIBUTE privTemplate[] =
+ {
+ { CKA_CLASS, &privClass, (CK_ULONG) sizeof(privClass) },
+ { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+ { CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_SENSITIVE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_EXTRACTABLE, &truevalue, (CK_ULONG) sizeof(truevalue) },
+ { CKA_SIGN, &truevalue, (CK_ULONG) sizeof(truevalue) }
+ };
+ CK_ATTRIBUTE *attr;
+ pk11_object_t *ec;
+ pk11_context_t *pk11_ctx;
+ isc_result_t ret;
+
+ REQUIRE(key->key_alg == DST_ALG_ECDSA256 ||
+ key->key_alg == DST_ALG_ECDSA384);
+ UNUSED(unused);
+ UNUSED(callback);
+
+ pk11_ctx = (pk11_context_t *) isc_mem_get(key->mctx,
+ sizeof(*pk11_ctx));
+ if (pk11_ctx == NULL)
+ return (ISC_R_NOMEMORY);
+ ret = pk11_get_session(pk11_ctx, OP_EC, true, false,
+ false, NULL, pk11_get_best_token(OP_EC));
+ if (ret != ISC_R_SUCCESS)
+ goto err;
+
+ ec = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*ec));
+ if (ec == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(ec, 0, sizeof(*ec));
+ key->keydata.pkey = ec;
+ ec->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 3);
+ if (ec->repr == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(ec->repr, 0, sizeof(*attr) * 3);
+ ec->attrcnt = 3;
+
+ attr = ec->repr;
+ attr[0].type = CKA_EC_PARAMS;
+ attr[1].type = CKA_EC_POINT;
+ attr[2].type = CKA_VALUE;
+
+ attr = &pubTemplate[5];
+ SETCURVE();
+
+ PK11_RET(pkcs_C_GenerateKeyPair,
+ (pk11_ctx->session, &mech,
+ pubTemplate, (CK_ULONG) 6,
+ privTemplate, (CK_ULONG) 7,
+ &pub, &priv),
+ DST_R_CRYPTOFAILURE);
+
+ attr = &pubTemplate[5];
+ FREECURVE();
+
+ attr = ec->repr;
+ SETCURVE();
+
+ attr++;
+ PK11_RET(pkcs_C_GetAttributeValue,
+ (pk11_ctx->session, pub, attr, 1),
+ DST_R_CRYPTOFAILURE);
+ attr->pValue = isc_mem_get(key->mctx, attr->ulValueLen);
+ if (attr->pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(attr->pValue, 0, attr->ulValueLen);
+ PK11_RET(pkcs_C_GetAttributeValue,
+ (pk11_ctx->session, pub, attr, 1),
+ DST_R_CRYPTOFAILURE);
+
+ attr++;
+ PK11_RET(pkcs_C_GetAttributeValue,
+ (pk11_ctx->session, priv, attr, 1),
+ DST_R_CRYPTOFAILURE);
+ attr->pValue = isc_mem_get(key->mctx, attr->ulValueLen);
+ if (attr->pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(attr->pValue, 0, attr->ulValueLen);
+ PK11_RET(pkcs_C_GetAttributeValue,
+ (pk11_ctx->session, priv, attr, 1),
+ DST_R_CRYPTOFAILURE);
+
+ (void) pkcs_C_DestroyObject(pk11_ctx->session, priv);
+ (void) pkcs_C_DestroyObject(pk11_ctx->session, pub);
+ pk11_return_session(pk11_ctx);
+ memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+ isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+ if (key->key_alg == DST_ALG_ECDSA256)
+ key->key_size = DNS_KEY_ECDSA256SIZE * 4;
+ else
+ key->key_size = DNS_KEY_ECDSA384SIZE * 4;
+
+ return (ISC_R_SUCCESS);
+
+ err:
+ pkcs11ecdsa_destroy(key);
+ if (priv != CK_INVALID_HANDLE)
+ (void) pkcs_C_DestroyObject(pk11_ctx->session, priv);
+ if (pub != CK_INVALID_HANDLE)
+ (void) pkcs_C_DestroyObject(pk11_ctx->session, pub);
+ pk11_return_session(pk11_ctx);
+ memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+ isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+ return (ret);
+}
+
+static bool
+pkcs11ecdsa_isprivate(const dst_key_t *key) {
+ pk11_object_t *ec = key->keydata.pkey;
+ CK_ATTRIBUTE *attr;
+
+ if (ec == NULL)
+ return (false);
+ attr = pk11_attribute_bytype(ec, CKA_VALUE);
+ return (attr != NULL || ec->ontoken);
+}
+
+static void
+pkcs11ecdsa_destroy(dst_key_t *key) {
+ pk11_object_t *ec = key->keydata.pkey;
+ CK_ATTRIBUTE *attr;
+
+ if (ec == NULL)
+ return;
+
+ INSIST((ec->object == CK_INVALID_HANDLE) || ec->ontoken);
+
+ for (attr = pk11_attribute_first(ec);
+ attr != NULL;
+ attr = pk11_attribute_next(ec, attr))
+ switch (attr->type) {
+ case CKA_LABEL:
+ case CKA_ID:
+ case CKA_EC_PARAMS:
+ case CKA_EC_POINT:
+ case CKA_VALUE:
+ FREECURVE();
+ break;
+ }
+ if (ec->repr != NULL) {
+ memset(ec->repr, 0, ec->attrcnt * sizeof(*attr));
+ isc_mem_put(key->mctx,
+ ec->repr,
+ ec->attrcnt * sizeof(*attr));
+ }
+ memset(ec, 0, sizeof(*ec));
+ isc_mem_put(key->mctx, ec, sizeof(*ec));
+ key->keydata.pkey = NULL;
+}
+
+static isc_result_t
+pkcs11ecdsa_todns(const dst_key_t *key, isc_buffer_t *data) {
+ pk11_object_t *ec;
+ isc_region_t r;
+ unsigned int len;
+ CK_ATTRIBUTE *attr;
+
+ REQUIRE(key->keydata.pkey != NULL);
+
+ if (key->key_alg == DST_ALG_ECDSA256)
+ len = DNS_KEY_ECDSA256SIZE;
+ else
+ len = DNS_KEY_ECDSA384SIZE;
+
+ ec = key->keydata.pkey;
+ attr = pk11_attribute_bytype(ec, CKA_EC_POINT);
+ if ((attr == NULL) ||
+ (attr->ulValueLen != len + 3) ||
+ (((CK_BYTE_PTR) attr->pValue)[0] != TAG_OCTECT_STRING) ||
+ (((CK_BYTE_PTR) attr->pValue)[1] != len + 1) ||
+ (((CK_BYTE_PTR) attr->pValue)[2] != UNCOMPRESSED))
+ return (ISC_R_FAILURE);
+
+ isc_buffer_availableregion(data, &r);
+ if (r.length < len)
+ return (ISC_R_NOSPACE);
+ memmove(r.base, (CK_BYTE_PTR) attr->pValue + 3, len);
+ isc_buffer_add(data, len);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+pkcs11ecdsa_fromdns(dst_key_t *key, isc_buffer_t *data) {
+ pk11_object_t *ec;
+ isc_region_t r;
+ unsigned int len;
+ CK_ATTRIBUTE *attr;
+
+ REQUIRE(key->key_alg == DST_ALG_ECDSA256 ||
+ key->key_alg == DST_ALG_ECDSA384);
+
+ if (key->key_alg == DST_ALG_ECDSA256)
+ len = DNS_KEY_ECDSA256SIZE;
+ else
+ len = DNS_KEY_ECDSA384SIZE;
+
+ isc_buffer_remainingregion(data, &r);
+ if (r.length == 0)
+ return (ISC_R_SUCCESS);
+ if (r.length != len)
+ return (DST_R_INVALIDPUBLICKEY);
+
+ ec = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*ec));
+ if (ec == NULL)
+ return (ISC_R_NOMEMORY);
+ memset(ec, 0, sizeof(*ec));
+ ec->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 2);
+ if (ec->repr == NULL)
+ goto nomemory;
+ ec->attrcnt = 2;
+
+ attr = ec->repr;
+ attr->type = CKA_EC_PARAMS;
+ if (key->key_alg == DST_ALG_ECDSA256) {
+ attr->pValue =
+ isc_mem_get(key->mctx, sizeof(pk11_ecc_prime256v1));
+ if (attr->pValue == NULL)
+ goto nomemory;
+ memmove(attr->pValue,
+ pk11_ecc_prime256v1, sizeof(pk11_ecc_prime256v1));
+ attr->ulValueLen = sizeof(pk11_ecc_prime256v1);
+ } else {
+ attr->pValue =
+ isc_mem_get(key->mctx, sizeof(pk11_ecc_secp384r1));
+ if (attr->pValue == NULL)
+ goto nomemory;
+ memmove(attr->pValue,
+ pk11_ecc_secp384r1, sizeof(pk11_ecc_secp384r1));
+ attr->ulValueLen = sizeof(pk11_ecc_secp384r1);
+ }
+
+ attr++;
+ attr->type = CKA_EC_POINT;
+ attr->pValue = isc_mem_get(key->mctx, len + 3);
+ if (attr->pValue == NULL)
+ goto nomemory;
+ ((CK_BYTE_PTR) attr->pValue)[0] = TAG_OCTECT_STRING;
+ ((CK_BYTE_PTR) attr->pValue)[1] = len + 1;
+ ((CK_BYTE_PTR) attr->pValue)[2] = UNCOMPRESSED;
+ memmove((CK_BYTE_PTR) attr->pValue + 3, r.base, len);
+ attr->ulValueLen = len + 3;
+
+ isc_buffer_forward(data, len);
+ key->keydata.pkey = ec;
+ key->key_size = len * 4;
+ return (ISC_R_SUCCESS);
+
+ nomemory:
+ for (attr = pk11_attribute_first(ec);
+ attr != NULL;
+ attr = pk11_attribute_next(ec, attr))
+ switch (attr->type) {
+ case CKA_EC_PARAMS:
+ case CKA_EC_POINT:
+ FREECURVE();
+ break;
+ }
+ if (ec->repr != NULL) {
+ memset(ec->repr, 0, ec->attrcnt * sizeof(*attr));
+ isc_mem_put(key->mctx,
+ ec->repr,
+ ec->attrcnt * sizeof(*attr));
+ }
+ memset(ec, 0, sizeof(*ec));
+ isc_mem_put(key->mctx, ec, sizeof(*ec));
+ return (ISC_R_NOMEMORY);
+}
+
+static isc_result_t
+pkcs11ecdsa_tofile(const dst_key_t *key, const char *directory) {
+ isc_result_t ret;
+ pk11_object_t *ec;
+ dst_private_t priv;
+ unsigned char *buf = NULL;
+ unsigned int i = 0;
+ CK_ATTRIBUTE *attr;
+
+ if (key->keydata.pkey == NULL)
+ return (DST_R_NULLKEY);
+
+ if (key->external) {
+ priv.nelements = 0;
+ return (dst__privstruct_writefile(key, &priv, directory));
+ }
+
+ ec = key->keydata.pkey;
+ attr = pk11_attribute_bytype(ec, CKA_VALUE);
+ if (attr != NULL) {
+ buf = isc_mem_get(key->mctx, attr->ulValueLen);
+ if (buf == NULL)
+ return (ISC_R_NOMEMORY);
+ priv.elements[i].tag = TAG_ECDSA_PRIVATEKEY;
+ priv.elements[i].length = (unsigned short) attr->ulValueLen;
+ memmove(buf, attr->pValue, attr->ulValueLen);
+ priv.elements[i].data = buf;
+ i++;
+ }
+
+ if (key->engine != NULL) {
+ priv.elements[i].tag = TAG_ECDSA_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_ECDSA_LABEL;
+ priv.elements[i].length = strlen(key->label) + 1;
+ priv.elements[i].data = (unsigned char *)key->label;
+ i++;
+ }
+
+ priv.nelements = i;
+ ret = dst__privstruct_writefile(key, &priv, directory);
+
+ if (buf != NULL) {
+ memset(buf, 0, attr->ulValueLen);
+ isc_mem_put(key->mctx, buf, attr->ulValueLen);
+ }
+ return (ret);
+}
+
+static isc_result_t
+pkcs11ecdsa_fetch(dst_key_t *key, const char *engine, const char *label,
+ dst_key_t *pub)
+{
+ CK_RV rv;
+ CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY;
+ CK_KEY_TYPE keyType = CKK_EC;
+ CK_ATTRIBUTE searchTemplate[] =
+ {
+ { CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+ { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+ { CKA_TOKEN, &truevalue, (CK_ULONG) sizeof(truevalue) },
+ { CKA_LABEL, NULL, 0 }
+ };
+ CK_ULONG cnt;
+ CK_ATTRIBUTE *attr;
+ CK_ATTRIBUTE *pubattr;
+ pk11_object_t *ec;
+ pk11_object_t *pubec;
+ pk11_context_t *pk11_ctx = NULL;
+ isc_result_t ret;
+
+ if (label == NULL)
+ return (DST_R_NOENGINE);
+
+ ec = key->keydata.pkey;
+ pubec = pub->keydata.pkey;
+
+ ec->object = CK_INVALID_HANDLE;
+ ec->ontoken = true;
+ ec->reqlogon = true;
+ ec->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 2);
+ if (ec->repr == NULL)
+ return (ISC_R_NOMEMORY);
+ memset(ec->repr, 0, sizeof(*attr) * 2);
+ ec->attrcnt = 2;
+ attr = ec->repr;
+
+ attr->type = CKA_EC_PARAMS;
+ pubattr = pk11_attribute_bytype(pubec, CKA_EC_PARAMS);
+ attr->pValue = isc_mem_get(key->mctx, pubattr->ulValueLen);
+ if (attr->pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(attr->pValue, pubattr->pValue, pubattr->ulValueLen);
+ attr->ulValueLen = pubattr->ulValueLen;
+ attr++;
+
+ attr->type = CKA_EC_POINT;
+ pubattr = pk11_attribute_bytype(pubec, CKA_EC_POINT);
+ attr->pValue = isc_mem_get(key->mctx, pubattr->ulValueLen);
+ if (attr->pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(attr->pValue, pubattr->pValue, pubattr->ulValueLen);
+ attr->ulValueLen = pubattr->ulValueLen;
+
+ ret = pk11_parse_uri(ec, label, key->mctx, OP_EC);
+ if (ret != ISC_R_SUCCESS)
+ goto err;
+
+ pk11_ctx = (pk11_context_t *) isc_mem_get(key->mctx,
+ sizeof(*pk11_ctx));
+ if (pk11_ctx == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ ret = pk11_get_session(pk11_ctx, OP_EC, true, false,
+ ec->reqlogon, NULL, ec->slot);
+ if (ret != ISC_R_SUCCESS)
+ goto err;
+
+ attr = pk11_attribute_bytype(ec, CKA_LABEL);
+ if (attr == NULL) {
+ attr = pk11_attribute_bytype(ec, CKA_ID);
+ INSIST(attr != NULL);
+ searchTemplate[3].type = CKA_ID;
+ }
+ searchTemplate[3].pValue = attr->pValue;
+ searchTemplate[3].ulValueLen = attr->ulValueLen;
+
+ PK11_RET(pkcs_C_FindObjectsInit,
+ (pk11_ctx->session, searchTemplate, (CK_ULONG) 4),
+ DST_R_CRYPTOFAILURE);
+ PK11_RET(pkcs_C_FindObjects,
+ (pk11_ctx->session, &ec->object, (CK_ULONG) 1, &cnt),
+ DST_R_CRYPTOFAILURE);
+ (void) pkcs_C_FindObjectsFinal(pk11_ctx->session);
+ if (cnt == 0)
+ DST_RET(ISC_R_NOTFOUND);
+ if (cnt > 1)
+ DST_RET(ISC_R_EXISTS);
+
+ if (engine != NULL) {
+ key->engine = isc_mem_strdup(key->mctx, engine);
+ 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);
+
+ pk11_return_session(pk11_ctx);
+ memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+ isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+ return (ISC_R_SUCCESS);
+
+ err:
+ if (pk11_ctx != NULL) {
+ pk11_return_session(pk11_ctx);
+ memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+ isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+ }
+ return (ret);
+}
+
+static isc_result_t
+pkcs11ecdsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
+ dst_private_t priv;
+ isc_result_t ret;
+ pk11_object_t *ec = NULL;
+ CK_ATTRIBUTE *attr, *pattr;
+ isc_mem_t *mctx = key->mctx;
+ unsigned int i;
+ const char *engine = NULL, *label = NULL;
+
+ REQUIRE(key->key_alg == DST_ALG_ECDSA256 ||
+ key->key_alg == DST_ALG_ECDSA384);
+
+ if ((pub == NULL) || (pub->keydata.pkey == NULL))
+ DST_RET(DST_R_INVALIDPRIVATEKEY);
+
+ /* read private key file */
+ ret = dst__privstruct_parse(key, DST_ALG_ECDSA256, lexer, mctx, &priv);
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+
+ if (key->external) {
+ if (priv.nelements != 0)
+ DST_RET(DST_R_INVALIDPRIVATEKEY);
+
+ key->keydata.pkey = pub->keydata.pkey;
+ pub->keydata.pkey = NULL;
+ key->key_size = pub->key_size;
+
+ dst__privstruct_free(&priv, mctx);
+ memset(&priv, 0, sizeof(priv));
+
+ return (ISC_R_SUCCESS);
+ }
+
+ for (i = 0; i < priv.nelements; i++) {
+ switch (priv.elements[i].tag) {
+ case TAG_ECDSA_ENGINE:
+ engine = (char *)priv.elements[i].data;
+ break;
+ case TAG_ECDSA_LABEL:
+ label = (char *)priv.elements[i].data;
+ break;
+ default:
+ break;
+ }
+ }
+ ec = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*ec));
+ if (ec == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(ec, 0, sizeof(*ec));
+ key->keydata.pkey = ec;
+
+ /* Is this key is stored in a HSM? See if we can fetch it. */
+ if ((label != NULL) || (engine != NULL)) {
+ ret = pkcs11ecdsa_fetch(key, engine, label, pub);
+ if (ret != ISC_R_SUCCESS)
+ goto err;
+ dst__privstruct_free(&priv, mctx);
+ memset(&priv, 0, sizeof(priv));
+ return (ret);
+ }
+
+ ec->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 3);
+ if (ec->repr == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(ec->repr, 0, sizeof(*attr) * 3);
+ ec->attrcnt = 3;
+
+ attr = ec->repr;
+ attr->type = CKA_EC_PARAMS;
+ pattr = pk11_attribute_bytype(pub->keydata.pkey, CKA_EC_PARAMS);
+ INSIST(pattr != NULL);
+ attr->pValue = isc_mem_get(key->mctx, pattr->ulValueLen);
+ if (attr->pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(attr->pValue, pattr->pValue, pattr->ulValueLen);
+ attr->ulValueLen = pattr->ulValueLen;
+
+ attr++;
+ attr->type = CKA_EC_POINT;
+ pattr = pk11_attribute_bytype(pub->keydata.pkey, CKA_EC_POINT);
+ INSIST(pattr != NULL);
+ attr->pValue = isc_mem_get(key->mctx, pattr->ulValueLen);
+ if (attr->pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(attr->pValue, pattr->pValue, pattr->ulValueLen);
+ attr->ulValueLen = pattr->ulValueLen;
+
+ attr++;
+ attr->type = CKA_VALUE;
+ attr->pValue = isc_mem_get(key->mctx, priv.elements[0].length);
+ if (attr->pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(attr->pValue, priv.elements[0].data, priv.elements[0].length);
+ attr->ulValueLen = priv.elements[0].length;
+
+ dst__privstruct_free(&priv, mctx);
+ memset(&priv, 0, sizeof(priv));
+ if (key->key_alg == DST_ALG_ECDSA256)
+ key->key_size = DNS_KEY_ECDSA256SIZE * 4;
+ else
+ key->key_size = DNS_KEY_ECDSA384SIZE * 4;
+
+ return (ISC_R_SUCCESS);
+
+ err:
+ pkcs11ecdsa_destroy(key);
+ dst__privstruct_free(&priv, mctx);
+ memset(&priv, 0, sizeof(priv));
+ return (ret);
+}
+
+static isc_result_t
+pkcs11ecdsa_fromlabel(dst_key_t *key, const char *engine, const char *label,
+ const char *pin)
+{
+ CK_RV rv;
+ CK_OBJECT_HANDLE hKey = CK_INVALID_HANDLE;
+ CK_OBJECT_CLASS keyClass = CKO_PUBLIC_KEY;
+ CK_KEY_TYPE keyType = CKK_EC;
+ CK_ATTRIBUTE searchTemplate[] =
+ {
+ { CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+ { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+ { CKA_TOKEN, &truevalue, (CK_ULONG) sizeof(truevalue) },
+ { CKA_LABEL, NULL, 0 }
+ };
+ CK_ULONG cnt;
+ CK_ATTRIBUTE *attr;
+ pk11_object_t *ec;
+ pk11_context_t *pk11_ctx = NULL;
+ isc_result_t ret;
+ unsigned int i;
+
+ UNUSED(pin);
+
+ ec = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*ec));
+ if (ec == NULL)
+ return (ISC_R_NOMEMORY);
+ memset(ec, 0, sizeof(*ec));
+ ec->object = CK_INVALID_HANDLE;
+ ec->ontoken = true;
+ ec->reqlogon = true;
+ key->keydata.pkey = ec;
+
+ ec->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 2);
+ if (ec->repr == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(ec->repr, 0, sizeof(*attr) * 2);
+ ec->attrcnt = 2;
+ attr = ec->repr;
+ attr[0].type = CKA_EC_PARAMS;
+ attr[1].type = CKA_EC_POINT;
+
+ ret = pk11_parse_uri(ec, label, key->mctx, OP_EC);
+ if (ret != ISC_R_SUCCESS)
+ goto err;
+
+ pk11_ctx = (pk11_context_t *) isc_mem_get(key->mctx,
+ sizeof(*pk11_ctx));
+ if (pk11_ctx == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ ret = pk11_get_session(pk11_ctx, OP_EC, true, false,
+ ec->reqlogon, NULL, ec->slot);
+ if (ret != ISC_R_SUCCESS)
+ goto err;
+
+ attr = pk11_attribute_bytype(ec, CKA_LABEL);
+ if (attr == NULL) {
+ attr = pk11_attribute_bytype(ec, CKA_ID);
+ INSIST(attr != NULL);
+ searchTemplate[3].type = CKA_ID;
+ }
+ searchTemplate[3].pValue = attr->pValue;
+ searchTemplate[3].ulValueLen = attr->ulValueLen;
+
+ PK11_RET(pkcs_C_FindObjectsInit,
+ (pk11_ctx->session, searchTemplate, (CK_ULONG) 4),
+ DST_R_CRYPTOFAILURE);
+ PK11_RET(pkcs_C_FindObjects,
+ (pk11_ctx->session, &hKey, (CK_ULONG) 1, &cnt),
+ DST_R_CRYPTOFAILURE);
+ (void) pkcs_C_FindObjectsFinal(pk11_ctx->session);
+ if (cnt == 0)
+ DST_RET(ISC_R_NOTFOUND);
+ if (cnt > 1)
+ DST_RET(ISC_R_EXISTS);
+
+ attr = ec->repr;
+ PK11_RET(pkcs_C_GetAttributeValue,
+ (pk11_ctx->session, hKey, attr, 2),
+ DST_R_CRYPTOFAILURE);
+ for (i = 0; i <= 1; i++) {
+ attr[i].pValue = isc_mem_get(key->mctx, attr[i].ulValueLen);
+ if (attr[i].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(attr[i].pValue, 0, attr[i].ulValueLen);
+ }
+ PK11_RET(pkcs_C_GetAttributeValue,
+ (pk11_ctx->session, hKey, attr, 2),
+ DST_R_CRYPTOFAILURE);
+
+ keyClass = CKO_PRIVATE_KEY;
+ PK11_RET(pkcs_C_FindObjectsInit,
+ (pk11_ctx->session, searchTemplate, (CK_ULONG) 4),
+ DST_R_CRYPTOFAILURE);
+ PK11_RET(pkcs_C_FindObjects,
+ (pk11_ctx->session, &ec->object, (CK_ULONG) 1, &cnt),
+ DST_R_CRYPTOFAILURE);
+ (void) pkcs_C_FindObjectsFinal(pk11_ctx->session);
+ if (cnt == 0)
+ DST_RET(ISC_R_NOTFOUND);
+ if (cnt > 1)
+ DST_RET(ISC_R_EXISTS);
+
+ if (engine != NULL) {
+ key->engine = isc_mem_strdup(key->mctx, engine);
+ 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);
+ if (key->key_alg == DST_ALG_ECDSA256)
+ key->key_size = DNS_KEY_ECDSA256SIZE * 4;
+ else
+ key->key_size = DNS_KEY_ECDSA384SIZE * 4;
+
+ pk11_return_session(pk11_ctx);
+ memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+ isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+ return (ISC_R_SUCCESS);
+
+ err:
+ pkcs11ecdsa_destroy(key);
+ if (pk11_ctx != NULL) {
+ pk11_return_session(pk11_ctx);
+ memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+ isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+ }
+ return (ret);
+}
+
+static dst_func_t pkcs11ecdsa_functions = {
+ pkcs11ecdsa_createctx,
+ NULL, /*%< createctx2 */
+ pkcs11ecdsa_destroyctx,
+ pkcs11ecdsa_adddata,
+ pkcs11ecdsa_sign,
+ pkcs11ecdsa_verify,
+ NULL, /*%< verify2 */
+ NULL, /*%< computesecret */
+ pkcs11ecdsa_compare,
+ NULL, /*%< paramcompare */
+ pkcs11ecdsa_generate,
+ pkcs11ecdsa_isprivate,
+ pkcs11ecdsa_destroy,
+ pkcs11ecdsa_todns,
+ pkcs11ecdsa_fromdns,
+ pkcs11ecdsa_tofile,
+ pkcs11ecdsa_parse,
+ NULL, /*%< cleanup */
+ pkcs11ecdsa_fromlabel,
+ NULL, /*%< dump */
+ NULL, /*%< restore */
+};
+
+isc_result_t
+dst__pkcs11ecdsa_init(dst_func_t **funcp) {
+ REQUIRE(funcp != NULL);
+ if (*funcp == NULL)
+ *funcp = &pkcs11ecdsa_functions;
+ return (ISC_R_SUCCESS);
+}
+
+#else /* PKCS11CRYPTO && HAVE_PKCS11_ECDSA */
+
+#include <isc/util.h>
+
+EMPTY_TRANSLATION_UNIT
+
+#endif /* PKCS11CRYPTO && HAVE_PKCS11_ECDSA */
+/*! \file */
diff --git a/lib/dns/pkcs11eddsa_link.c b/lib/dns/pkcs11eddsa_link.c
new file mode 100644
index 0000000..015d0e8
--- /dev/null
+++ b/lib/dns/pkcs11eddsa_link.c
@@ -0,0 +1,1185 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <config.h>
+
+#if defined(PKCS11CRYPTO) && \
+ defined(HAVE_PKCS11_ED25519) || defined(HAVE_PKCS11_ED448)
+
+#include <stdbool.h>
+
+#include <isc/entropy.h>
+#include <isc/mem.h>
+#include <isc/safe.h>
+#include <isc/sha2.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dns/keyvalues.h>
+#include <dst/result.h>
+
+#include "dst_internal.h"
+#include "dst_parse.h"
+#include "dst_pkcs11.h"
+
+#include <pk11/pk11.h>
+#include <pk11/internal.h>
+#define WANT_ECC_CURVES
+#include <pk11/constants.h>
+
+#include <pkcs11/pkcs11.h>
+#include <pkcs11/eddsa.h>
+
+/*
+ * FIPS 186-3 EDDSA keys:
+ * mechanisms:
+ * CKM_EDDSA,
+ * CKM_EDDSA_KEY_PAIR_GEN
+ * domain parameters:
+ * CKA_EC_PARAMS (choice with OID namedCurve)
+ * public keys:
+ * object class CKO_PUBLIC_KEY
+ * key type CKK_EDDSA
+ * attribute CKA_EC_PARAMS (choice with OID namedCurve)
+ * attribute CKA_EC_POINT (big int A, CKA_VALUE on the token)
+ * private keys:
+ * object class CKO_PRIVATE_KEY
+ * key type CKK_EDDSA
+ * attribute CKA_EC_PARAMS (choice with OID namedCurve)
+ * attribute CKA_VALUE (big int k)
+ */
+
+#define DST_RET(a) {ret = a; goto err;}
+
+static CK_BBOOL truevalue = TRUE;
+static CK_BBOOL falsevalue = FALSE;
+
+static isc_result_t pkcs11eddsa_todns(const dst_key_t *key,
+ isc_buffer_t *data);
+static void pkcs11eddsa_destroy(dst_key_t *key);
+static isc_result_t pkcs11eddsa_fetch(dst_key_t *key, const char *engine,
+ const char *label, dst_key_t *pub);
+
+static isc_result_t
+pkcs11eddsa_createctx(dst_key_t *key, dst_context_t *dctx) {
+ isc_buffer_t *buf = NULL;
+ isc_result_t result;
+
+ UNUSED(key);
+ REQUIRE(dctx->key->key_alg == DST_ALG_ED25519 ||
+ dctx->key->key_alg == DST_ALG_ED448);
+
+ result = isc_buffer_allocate(dctx->mctx, &buf, 16);
+ isc_buffer_setautorealloc(buf, true);
+ dctx->ctxdata.generic = buf;
+
+ return (result);
+}
+
+static void
+pkcs11eddsa_destroyctx(dst_context_t *dctx) {
+ isc_buffer_t *buf = (isc_buffer_t *) dctx->ctxdata.generic;
+
+ REQUIRE(dctx->key->key_alg == DST_ALG_ED25519 ||
+ dctx->key->key_alg == DST_ALG_ED448);
+ if (buf != NULL)
+ isc_buffer_free(&buf);
+ dctx->ctxdata.generic = NULL;
+}
+
+static isc_result_t
+pkcs11eddsa_adddata(dst_context_t *dctx, const isc_region_t *data) {
+ isc_buffer_t *buf = (isc_buffer_t *) dctx->ctxdata.generic;
+ isc_buffer_t *nbuf = NULL;
+ isc_region_t r;
+ unsigned int length;
+ isc_result_t result;
+
+ REQUIRE(dctx->key->key_alg == DST_ALG_ED25519 ||
+ dctx->key->key_alg == DST_ALG_ED448);
+
+ result = isc_buffer_copyregion(buf, data);
+ if (result == ISC_R_SUCCESS)
+ return (ISC_R_SUCCESS);
+
+ length = isc_buffer_length(buf) + data->length + 64;
+ result = isc_buffer_allocate(dctx->mctx, &nbuf, length);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ isc_buffer_usedregion(buf, &r);
+ (void) isc_buffer_copyregion(nbuf, &r);
+ (void) isc_buffer_copyregion(nbuf, data);
+ isc_buffer_free(&buf);
+ dctx->ctxdata.generic = nbuf;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+pkcs11eddsa_sign(dst_context_t *dctx, isc_buffer_t *sig) {
+ isc_buffer_t *buf = (isc_buffer_t *) dctx->ctxdata.generic;
+ CK_RV rv;
+ CK_MECHANISM mech = { CKM_EDDSA, NULL, 0 };
+ CK_OBJECT_HANDLE hKey = CK_INVALID_HANDLE;
+ CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY;
+ CK_KEY_TYPE keyType = CKK_EDDSA;
+ CK_ATTRIBUTE keyTemplate[] =
+ {
+ { CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+ { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+ { CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_SIGN, &truevalue, (CK_ULONG) sizeof(truevalue) },
+ { CKA_EC_PARAMS, NULL, 0 },
+ { CKA_VALUE, NULL, 0 }
+ };
+ CK_ATTRIBUTE *attr;
+ CK_ULONG siglen;
+ CK_SLOT_ID slotid;
+ pk11_context_t *pk11_ctx;
+ dst_key_t *key = dctx->key;
+ pk11_object_t *ec = key->keydata.pkey;
+ isc_region_t t;
+ isc_region_t r;
+ isc_result_t ret = ISC_R_SUCCESS;
+ unsigned int i;
+
+ REQUIRE(key->key_alg == DST_ALG_ED25519 ||
+ key->key_alg == DST_ALG_ED448);
+ REQUIRE(ec != NULL);
+
+ if (key->key_alg == DST_ALG_ED25519)
+ siglen = DNS_SIG_ED25519SIZE;
+ else
+ siglen = DNS_SIG_ED448SIZE;
+
+ pk11_ctx = (pk11_context_t *) isc_mem_get(dctx->mctx,
+ sizeof(*pk11_ctx));
+ if (pk11_ctx == NULL)
+ return (ISC_R_NOMEMORY);
+ memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+ if (ec->ontoken && (dctx->use == DO_SIGN))
+ slotid = ec->slot;
+ else
+ slotid = pk11_get_best_token(OP_EC);
+ ret = pk11_get_session(pk11_ctx, OP_EC, true, false,
+ ec->reqlogon, NULL, slotid);
+ if (ret != ISC_R_SUCCESS)
+ goto err;
+
+ isc_buffer_availableregion(sig, &r);
+ if (r.length < siglen)
+ DST_RET(ISC_R_NOSPACE);
+
+ if (ec->ontoken && (ec->object != CK_INVALID_HANDLE)) {
+ pk11_ctx->ontoken = ec->ontoken;
+ pk11_ctx->object = ec->object;
+ goto token_key;
+ }
+
+ for (attr = pk11_attribute_first(ec);
+ attr != NULL;
+ attr = pk11_attribute_next(ec, attr))
+ switch (attr->type) {
+ case CKA_EC_PARAMS:
+ INSIST(keyTemplate[5].type == attr->type);
+ keyTemplate[5].pValue = isc_mem_get(dctx->mctx,
+ attr->ulValueLen);
+ if (keyTemplate[5].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[5].pValue, attr->pValue,
+ attr->ulValueLen);
+ keyTemplate[5].ulValueLen = attr->ulValueLen;
+ break;
+ case CKA_VALUE:
+ INSIST(keyTemplate[6].type == attr->type);
+ keyTemplate[6].pValue = isc_mem_get(dctx->mctx,
+ attr->ulValueLen);
+ if (keyTemplate[6].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[6].pValue, attr->pValue,
+ attr->ulValueLen);
+ keyTemplate[6].ulValueLen = attr->ulValueLen;
+ break;
+ }
+ pk11_ctx->object = CK_INVALID_HANDLE;
+ pk11_ctx->ontoken = false;
+ PK11_RET(pkcs_C_CreateObject,
+ (pk11_ctx->session,
+ keyTemplate, (CK_ULONG) 7,
+ &hKey),
+ ISC_R_FAILURE);
+
+ token_key:
+
+ PK11_RET(pkcs_C_SignInit,
+ (pk11_ctx->session, &mech,
+ pk11_ctx->ontoken ? pk11_ctx->object : hKey),
+ ISC_R_FAILURE);
+
+ isc_buffer_usedregion(buf, &t);
+
+ PK11_RET(pkcs_C_Sign,
+ (pk11_ctx->session,
+ (CK_BYTE_PTR) t.base, (CK_ULONG) t.length,
+ (CK_BYTE_PTR) r.base, &siglen),
+ DST_R_SIGNFAILURE);
+
+ isc_buffer_add(sig, (unsigned int) siglen);
+
+ err:
+
+ if (hKey != CK_INVALID_HANDLE)
+ (void) pkcs_C_DestroyObject(pk11_ctx->session, hKey);
+ for (i = 5; i <= 6; i++)
+ if (keyTemplate[i].pValue != NULL) {
+ memset(keyTemplate[i].pValue, 0,
+ keyTemplate[i].ulValueLen);
+ isc_mem_put(dctx->mctx,
+ keyTemplate[i].pValue,
+ keyTemplate[i].ulValueLen);
+ }
+ pk11_return_session(pk11_ctx);
+ memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+ isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx));
+ isc_buffer_free(&buf);
+ dctx->ctxdata.generic = NULL;
+
+ return (ret);
+}
+
+static isc_result_t
+pkcs11eddsa_verify(dst_context_t *dctx, const isc_region_t *sig) {
+ isc_buffer_t *buf = (isc_buffer_t *) dctx->ctxdata.generic;
+ CK_RV rv;
+ CK_MECHANISM mech = { CKM_EDDSA, NULL, 0 };
+ CK_OBJECT_HANDLE hKey = CK_INVALID_HANDLE;
+ CK_OBJECT_CLASS keyClass = CKO_PUBLIC_KEY;
+ CK_KEY_TYPE keyType = CKK_EDDSA;
+ CK_ATTRIBUTE keyTemplate[] =
+ {
+ { CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+ { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+ { CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_VERIFY, &truevalue, (CK_ULONG) sizeof(truevalue) },
+ { CKA_EC_PARAMS, NULL, 0 },
+ { CKA_VALUE, NULL, 0 }
+ };
+ CK_ATTRIBUTE *attr;
+ CK_SLOT_ID slotid;
+ pk11_context_t *pk11_ctx;
+ dst_key_t *key = dctx->key;
+ pk11_object_t *ec = key->keydata.pkey;
+ isc_region_t t;
+ isc_result_t ret = ISC_R_SUCCESS;
+ unsigned int i;
+
+ REQUIRE(key->key_alg == DST_ALG_ED25519 ||
+ key->key_alg == DST_ALG_ED448);
+ REQUIRE(ec != NULL);
+
+ pk11_ctx = (pk11_context_t *) isc_mem_get(dctx->mctx,
+ sizeof(*pk11_ctx));
+ if (pk11_ctx == NULL)
+ return (ISC_R_NOMEMORY);
+ memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+ if (ec->ontoken && (dctx->use == DO_SIGN))
+ slotid = ec->slot;
+ else
+ slotid = pk11_get_best_token(OP_EC);
+ ret = pk11_get_session(pk11_ctx, OP_EC, true, false,
+ ec->reqlogon, NULL, slotid);
+ if (ret != ISC_R_SUCCESS)
+ goto err;
+
+ for (attr = pk11_attribute_first(ec);
+ attr != NULL;
+ attr = pk11_attribute_next(ec, attr))
+ switch (attr->type) {
+ case CKA_EC_PARAMS:
+ INSIST(keyTemplate[5].type == attr->type);
+ keyTemplate[5].pValue = isc_mem_get(dctx->mctx,
+ attr->ulValueLen);
+ if (keyTemplate[5].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[5].pValue, attr->pValue,
+ attr->ulValueLen);
+ keyTemplate[5].ulValueLen = attr->ulValueLen;
+ break;
+ case CKA_EC_POINT:
+ /* keyTemplate[6].type is CKA_VALUE */
+ keyTemplate[6].pValue = isc_mem_get(dctx->mctx,
+ attr->ulValueLen);
+ if (keyTemplate[6].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[6].pValue, attr->pValue,
+ attr->ulValueLen);
+ keyTemplate[6].ulValueLen = attr->ulValueLen;
+ break;
+ }
+ pk11_ctx->object = CK_INVALID_HANDLE;
+ pk11_ctx->ontoken = false;
+ PK11_RET(pkcs_C_CreateObject,
+ (pk11_ctx->session,
+ keyTemplate, (CK_ULONG) 7,
+ &hKey),
+ ISC_R_FAILURE);
+
+ PK11_RET(pkcs_C_VerifyInit,
+ (pk11_ctx->session, &mech, hKey),
+ ISC_R_FAILURE);
+
+ isc_buffer_usedregion(buf, &t);
+
+ PK11_RET(pkcs_C_Verify,
+ (pk11_ctx->session,
+ (CK_BYTE_PTR) t.base, (CK_ULONG) t.length,
+ (CK_BYTE_PTR) sig->base, (CK_ULONG) sig->length),
+ DST_R_VERIFYFAILURE);
+
+ err:
+
+ if (hKey != CK_INVALID_HANDLE)
+ (void) pkcs_C_DestroyObject(pk11_ctx->session, hKey);
+ for (i = 5; i <= 6; i++)
+ if (keyTemplate[i].pValue != NULL) {
+ memset(keyTemplate[i].pValue, 0,
+ keyTemplate[i].ulValueLen);
+ isc_mem_put(dctx->mctx,
+ keyTemplate[i].pValue,
+ keyTemplate[i].ulValueLen);
+ }
+ pk11_return_session(pk11_ctx);
+ memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+ isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx));
+ isc_buffer_free(&buf);
+ dctx->ctxdata.generic = NULL;
+
+ return (ret);
+}
+
+static bool
+pkcs11eddsa_compare(const dst_key_t *key1, const dst_key_t *key2) {
+ pk11_object_t *ec1, *ec2;
+ CK_ATTRIBUTE *attr1, *attr2;
+
+ ec1 = key1->keydata.pkey;
+ ec2 = key2->keydata.pkey;
+
+ if ((ec1 == NULL) && (ec2 == NULL))
+ return (true);
+ else if ((ec1 == NULL) || (ec2 == NULL))
+ return (false);
+
+ attr1 = pk11_attribute_bytype(ec1, CKA_EC_PARAMS);
+ attr2 = pk11_attribute_bytype(ec2, CKA_EC_PARAMS);
+ if ((attr1 == NULL) && (attr2 == NULL))
+ return (true);
+ else if ((attr1 == NULL) || (attr2 == NULL) ||
+ (attr1->ulValueLen != attr2->ulValueLen) ||
+ !isc_safe_memequal(attr1->pValue, attr2->pValue,
+ attr1->ulValueLen))
+ return (false);
+
+ attr1 = pk11_attribute_bytype(ec1, CKA_EC_POINT);
+ attr2 = pk11_attribute_bytype(ec2, CKA_EC_POINT);
+ if ((attr1 == NULL) && (attr2 == NULL))
+ return (true);
+ else if ((attr1 == NULL) || (attr2 == NULL) ||
+ (attr1->ulValueLen != attr2->ulValueLen) ||
+ !isc_safe_memequal(attr1->pValue, attr2->pValue,
+ attr1->ulValueLen))
+ return (false);
+
+ attr1 = pk11_attribute_bytype(ec1, CKA_VALUE);
+ attr2 = pk11_attribute_bytype(ec2, CKA_VALUE);
+ if (((attr1 != NULL) || (attr2 != NULL)) &&
+ ((attr1 == NULL) || (attr2 == NULL) ||
+ (attr1->ulValueLen != attr2->ulValueLen) ||
+ !isc_safe_memequal(attr1->pValue, attr2->pValue,
+ attr1->ulValueLen)))
+ return (false);
+
+ if (!ec1->ontoken && !ec2->ontoken)
+ return (true);
+ else if (ec1->ontoken || ec2->ontoken ||
+ (ec1->object != ec2->object))
+ return (false);
+
+ return (true);
+}
+
+#define SETCURVE() \
+ if (key->key_alg == DST_ALG_ED25519) { \
+ attr->pValue = isc_mem_get(key->mctx, \
+ sizeof(pk11_ecc_ed25519)); \
+ if (attr->pValue == NULL) \
+ DST_RET(ISC_R_NOMEMORY); \
+ memmove(attr->pValue, \
+ pk11_ecc_ed25519, sizeof(pk11_ecc_ed25519)); \
+ attr->ulValueLen = sizeof(pk11_ecc_ed25519); \
+ } else { \
+ attr->pValue = isc_mem_get(key->mctx, \
+ sizeof(pk11_ecc_ed448)); \
+ if (attr->pValue == NULL) \
+ DST_RET(ISC_R_NOMEMORY); \
+ memmove(attr->pValue, \
+ pk11_ecc_ed448, sizeof(pk11_ecc_ed448)); \
+ attr->ulValueLen = sizeof(pk11_ecc_ed448); \
+ }
+
+#define FREECURVE() \
+ if (attr->pValue != NULL) { \
+ memset(attr->pValue, 0, attr->ulValueLen); \
+ isc_mem_put(key->mctx, attr->pValue, attr->ulValueLen); \
+ attr->pValue = NULL; \
+ }
+
+static isc_result_t
+pkcs11eddsa_generate(dst_key_t *key, int unused, void (*callback)(int)) {
+ CK_RV rv;
+ CK_MECHANISM mech = { CKM_EDDSA_KEY_PAIR_GEN, NULL, 0 };
+ CK_OBJECT_HANDLE pub = CK_INVALID_HANDLE;
+ CK_OBJECT_CLASS pubClass = CKO_PUBLIC_KEY;
+ CK_KEY_TYPE keyType = CKK_EDDSA;
+ CK_ATTRIBUTE pubTemplate[] =
+ {
+ { CKA_CLASS, &pubClass, (CK_ULONG) sizeof(pubClass) },
+ { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+ { CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_VERIFY, &truevalue, (CK_ULONG) sizeof(truevalue) },
+ { CKA_EC_PARAMS, NULL, 0 }
+ };
+ CK_OBJECT_HANDLE priv = CK_INVALID_HANDLE;
+ CK_OBJECT_HANDLE privClass = CKO_PRIVATE_KEY;
+ CK_ATTRIBUTE privTemplate[] =
+ {
+ { CKA_CLASS, &privClass, (CK_ULONG) sizeof(privClass) },
+ { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+ { CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_SENSITIVE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_EXTRACTABLE, &truevalue, (CK_ULONG) sizeof(truevalue) },
+ { CKA_SIGN, &truevalue, (CK_ULONG) sizeof(truevalue) }
+ };
+ CK_ATTRIBUTE *attr;
+ pk11_object_t *ec;
+ pk11_context_t *pk11_ctx;
+ isc_result_t ret;
+
+ REQUIRE(key->key_alg == DST_ALG_ED25519 ||
+ key->key_alg == DST_ALG_ED448);
+ UNUSED(unused);
+ UNUSED(callback);
+
+ pk11_ctx = (pk11_context_t *) isc_mem_get(key->mctx,
+ sizeof(*pk11_ctx));
+ if (pk11_ctx == NULL)
+ return (ISC_R_NOMEMORY);
+ ret = pk11_get_session(pk11_ctx, OP_EC, true, false,
+ false, NULL, pk11_get_best_token(OP_EC));
+ if (ret != ISC_R_SUCCESS)
+ goto err;
+
+ ec = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*ec));
+ if (ec == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(ec, 0, sizeof(*ec));
+ key->keydata.pkey = ec;
+ ec->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 3);
+ if (ec->repr == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(ec->repr, 0, sizeof(*attr) * 3);
+ ec->attrcnt = 3;
+
+ attr = ec->repr;
+ attr[0].type = CKA_EC_PARAMS;
+ attr[1].type = CKA_VALUE;
+ attr[2].type = CKA_VALUE;
+
+ attr = &pubTemplate[5];
+ SETCURVE();
+
+ PK11_RET(pkcs_C_GenerateKeyPair,
+ (pk11_ctx->session, &mech,
+ pubTemplate, (CK_ULONG) 6,
+ privTemplate, (CK_ULONG) 7,
+ &pub, &priv),
+ DST_R_CRYPTOFAILURE);
+
+ attr = &pubTemplate[5];
+ FREECURVE();
+
+ attr = ec->repr;
+ SETCURVE();
+
+ attr++;
+ PK11_RET(pkcs_C_GetAttributeValue,
+ (pk11_ctx->session, pub, attr, 1),
+ DST_R_CRYPTOFAILURE);
+ attr->pValue = isc_mem_get(key->mctx, attr->ulValueLen);
+ if (attr->pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(attr->pValue, 0, attr->ulValueLen);
+ PK11_RET(pkcs_C_GetAttributeValue,
+ (pk11_ctx->session, pub, attr, 1),
+ DST_R_CRYPTOFAILURE);
+ attr->type = CKA_EC_POINT;
+
+ attr++;
+ PK11_RET(pkcs_C_GetAttributeValue,
+ (pk11_ctx->session, priv, attr, 1),
+ DST_R_CRYPTOFAILURE);
+ attr->pValue = isc_mem_get(key->mctx, attr->ulValueLen);
+ if (attr->pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(attr->pValue, 0, attr->ulValueLen);
+ PK11_RET(pkcs_C_GetAttributeValue,
+ (pk11_ctx->session, priv, attr, 1),
+ DST_R_CRYPTOFAILURE);
+
+ (void) pkcs_C_DestroyObject(pk11_ctx->session, priv);
+ (void) pkcs_C_DestroyObject(pk11_ctx->session, pub);
+ pk11_return_session(pk11_ctx);
+ memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+ isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+ if (key->key_alg == DST_ALG_ED25519)
+ key->key_size = DNS_KEY_ED25519SIZE;
+ else
+ key->key_size = DNS_KEY_ED448SIZE;
+
+ return (ISC_R_SUCCESS);
+
+ err:
+ pkcs11eddsa_destroy(key);
+ if (priv != CK_INVALID_HANDLE)
+ (void) pkcs_C_DestroyObject(pk11_ctx->session, priv);
+ if (pub != CK_INVALID_HANDLE)
+ (void) pkcs_C_DestroyObject(pk11_ctx->session, pub);
+ pk11_return_session(pk11_ctx);
+ memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+ isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+ return (ret);
+}
+
+static bool
+pkcs11eddsa_isprivate(const dst_key_t *key) {
+ pk11_object_t *ec = key->keydata.pkey;
+ CK_ATTRIBUTE *attr;
+
+ if (ec == NULL)
+ return (false);
+ attr = pk11_attribute_bytype(ec, CKA_VALUE);
+ return (attr != NULL || ec->ontoken);
+}
+
+static void
+pkcs11eddsa_destroy(dst_key_t *key) {
+ pk11_object_t *ec = key->keydata.pkey;
+ CK_ATTRIBUTE *attr;
+
+ if (ec == NULL)
+ return;
+
+ INSIST((ec->object == CK_INVALID_HANDLE) || ec->ontoken);
+
+ for (attr = pk11_attribute_first(ec);
+ attr != NULL;
+ attr = pk11_attribute_next(ec, attr))
+ switch (attr->type) {
+ case CKA_LABEL:
+ case CKA_ID:
+ case CKA_EC_PARAMS:
+ case CKA_EC_POINT:
+ case CKA_VALUE:
+ FREECURVE();
+ break;
+ }
+ if (ec->repr != NULL) {
+ memset(ec->repr, 0, ec->attrcnt * sizeof(*attr));
+ isc_mem_put(key->mctx,
+ ec->repr,
+ ec->attrcnt * sizeof(*attr));
+ }
+ memset(ec, 0, sizeof(*ec));
+ isc_mem_put(key->mctx, ec, sizeof(*ec));
+ key->keydata.pkey = NULL;
+}
+
+static isc_result_t
+pkcs11eddsa_todns(const dst_key_t *key, isc_buffer_t *data) {
+ pk11_object_t *ec;
+ isc_region_t r;
+ unsigned int len;
+ CK_ATTRIBUTE *attr;
+
+ REQUIRE(key->keydata.pkey != NULL);
+
+ if (key->key_alg == DST_ALG_ED25519)
+ len = DNS_KEY_ED25519SIZE;
+ else
+ len = DNS_KEY_ED448SIZE;
+
+ ec = key->keydata.pkey;
+ attr = pk11_attribute_bytype(ec, CKA_EC_POINT);
+ if ((attr == NULL) || (attr->ulValueLen != len))
+ return (ISC_R_FAILURE);
+
+ isc_buffer_availableregion(data, &r);
+ if (r.length < len)
+ return (ISC_R_NOSPACE);
+ memmove(r.base, (CK_BYTE_PTR) attr->pValue, len);
+ isc_buffer_add(data, len);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+pkcs11eddsa_fromdns(dst_key_t *key, isc_buffer_t *data) {
+ pk11_object_t *ec;
+ isc_region_t r;
+ unsigned int len;
+ CK_ATTRIBUTE *attr;
+
+ REQUIRE(key->key_alg == DST_ALG_ED25519 ||
+ key->key_alg == DST_ALG_ED448);
+
+ if (key->key_alg == DST_ALG_ED25519)
+ len = DNS_KEY_ED25519SIZE;
+ else
+ len = DNS_KEY_ED448SIZE;
+
+ isc_buffer_remainingregion(data, &r);
+ if (r.length == 0)
+ return (ISC_R_SUCCESS);
+ if (r.length != len)
+ return (DST_R_INVALIDPUBLICKEY);
+
+ ec = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*ec));
+ if (ec == NULL)
+ return (ISC_R_NOMEMORY);
+ memset(ec, 0, sizeof(*ec));
+ ec->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 2);
+ if (ec->repr == NULL)
+ goto nomemory;
+ ec->attrcnt = 2;
+
+ attr = ec->repr;
+ attr->type = CKA_EC_PARAMS;
+ if (key->key_alg == DST_ALG_ED25519) {
+ attr->pValue =
+ isc_mem_get(key->mctx, sizeof(pk11_ecc_ed25519));
+ if (attr->pValue == NULL)
+ goto nomemory;
+ memmove(attr->pValue,
+ pk11_ecc_ed25519, sizeof(pk11_ecc_ed25519));
+ attr->ulValueLen = sizeof(pk11_ecc_ed25519);
+ } else {
+ attr->pValue =
+ isc_mem_get(key->mctx, sizeof(pk11_ecc_ed448));
+ if (attr->pValue == NULL)
+ goto nomemory;
+ memmove(attr->pValue,
+ pk11_ecc_ed448, sizeof(pk11_ecc_ed448));
+ attr->ulValueLen = sizeof(pk11_ecc_ed448);
+ }
+
+ attr++;
+ attr->type = CKA_EC_POINT;
+ attr->pValue = isc_mem_get(key->mctx, len);
+ if (attr->pValue == NULL)
+ goto nomemory;
+ memmove((CK_BYTE_PTR) attr->pValue, r.base, len);
+ attr->ulValueLen = len;
+
+ isc_buffer_forward(data, len);
+ key->keydata.pkey = ec;
+ key->key_size = len;
+ return (ISC_R_SUCCESS);
+
+ nomemory:
+ for (attr = pk11_attribute_first(ec);
+ attr != NULL;
+ attr = pk11_attribute_next(ec, attr))
+ switch (attr->type) {
+ case CKA_EC_PARAMS:
+ case CKA_EC_POINT:
+ FREECURVE();
+ break;
+ }
+ if (ec->repr != NULL) {
+ memset(ec->repr, 0, ec->attrcnt * sizeof(*attr));
+ isc_mem_put(key->mctx,
+ ec->repr,
+ ec->attrcnt * sizeof(*attr));
+ }
+ memset(ec, 0, sizeof(*ec));
+ isc_mem_put(key->mctx, ec, sizeof(*ec));
+ return (ISC_R_NOMEMORY);
+}
+
+static isc_result_t
+pkcs11eddsa_tofile(const dst_key_t *key, const char *directory) {
+ isc_result_t ret;
+ pk11_object_t *ec;
+ dst_private_t priv;
+ unsigned char *buf = NULL;
+ unsigned int i = 0;
+ CK_ATTRIBUTE *attr;
+
+ if (key->keydata.pkey == NULL)
+ return (DST_R_NULLKEY);
+
+ if (key->external) {
+ priv.nelements = 0;
+ return (dst__privstruct_writefile(key, &priv, directory));
+ }
+
+ ec = key->keydata.pkey;
+ attr = pk11_attribute_bytype(ec, CKA_VALUE);
+ if (attr != NULL) {
+ buf = isc_mem_get(key->mctx, attr->ulValueLen);
+ if (buf == NULL)
+ return (ISC_R_NOMEMORY);
+ priv.elements[i].tag = TAG_EDDSA_PRIVATEKEY;
+ priv.elements[i].length = (unsigned short) attr->ulValueLen;
+ memmove(buf, attr->pValue, attr->ulValueLen);
+ priv.elements[i].data = buf;
+ i++;
+ }
+
+ if (key->engine != NULL) {
+ priv.elements[i].tag = TAG_EDDSA_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_EDDSA_LABEL;
+ priv.elements[i].length = strlen(key->label) + 1;
+ priv.elements[i].data = (unsigned char *)key->label;
+ i++;
+ }
+
+ priv.nelements = i;
+ ret = dst__privstruct_writefile(key, &priv, directory);
+
+ if (buf != NULL) {
+ memset(buf, 0, attr->ulValueLen);
+ isc_mem_put(key->mctx, buf, attr->ulValueLen);
+ }
+ return (ret);
+}
+
+static isc_result_t
+pkcs11eddsa_fetch(dst_key_t *key, const char *engine, const char *label,
+ dst_key_t *pub)
+{
+ CK_RV rv;
+ CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY;
+ CK_KEY_TYPE keyType = CKK_EDDSA;
+ CK_ATTRIBUTE searchTemplate[] =
+ {
+ { CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+ { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+ { CKA_TOKEN, &truevalue, (CK_ULONG) sizeof(truevalue) },
+ { CKA_LABEL, NULL, 0 }
+ };
+ CK_ULONG cnt;
+ CK_ATTRIBUTE *attr;
+ CK_ATTRIBUTE *pubattr;
+ pk11_object_t *ec;
+ pk11_object_t *pubec;
+ pk11_context_t *pk11_ctx = NULL;
+ isc_result_t ret;
+
+ if (label == NULL)
+ return (DST_R_NOENGINE);
+
+ ec = key->keydata.pkey;
+ pubec = pub->keydata.pkey;
+
+ ec->object = CK_INVALID_HANDLE;
+ ec->ontoken = true;
+ ec->reqlogon = true;
+ ec->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 2);
+ if (ec->repr == NULL)
+ return (ISC_R_NOMEMORY);
+ memset(ec->repr, 0, sizeof(*attr) * 2);
+ ec->attrcnt = 2;
+ attr = ec->repr;
+
+ attr->type = CKA_EC_PARAMS;
+ pubattr = pk11_attribute_bytype(pubec, CKA_EC_PARAMS);
+ attr->pValue = isc_mem_get(key->mctx, pubattr->ulValueLen);
+ if (attr->pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(attr->pValue, pubattr->pValue, pubattr->ulValueLen);
+ attr->ulValueLen = pubattr->ulValueLen;
+ attr++;
+
+ attr->type = CKA_EC_POINT;
+ pubattr = pk11_attribute_bytype(pubec, CKA_EC_POINT);
+ attr->pValue = isc_mem_get(key->mctx, pubattr->ulValueLen);
+ if (attr->pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(attr->pValue, pubattr->pValue, pubattr->ulValueLen);
+ attr->ulValueLen = pubattr->ulValueLen;
+
+ ret = pk11_parse_uri(ec, label, key->mctx, OP_EC);
+ if (ret != ISC_R_SUCCESS)
+ goto err;
+
+ pk11_ctx = (pk11_context_t *) isc_mem_get(key->mctx,
+ sizeof(*pk11_ctx));
+ if (pk11_ctx == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ ret = pk11_get_session(pk11_ctx, OP_EC, true, false,
+ ec->reqlogon, NULL, ec->slot);
+ if (ret != ISC_R_SUCCESS)
+ goto err;
+
+ attr = pk11_attribute_bytype(ec, CKA_LABEL);
+ if (attr == NULL) {
+ attr = pk11_attribute_bytype(ec, CKA_ID);
+ INSIST(attr != NULL);
+ searchTemplate[3].type = CKA_ID;
+ }
+ searchTemplate[3].pValue = attr->pValue;
+ searchTemplate[3].ulValueLen = attr->ulValueLen;
+
+ PK11_RET(pkcs_C_FindObjectsInit,
+ (pk11_ctx->session, searchTemplate, (CK_ULONG) 4),
+ DST_R_CRYPTOFAILURE);
+ PK11_RET(pkcs_C_FindObjects,
+ (pk11_ctx->session, &ec->object, (CK_ULONG) 1, &cnt),
+ DST_R_CRYPTOFAILURE);
+ (void) pkcs_C_FindObjectsFinal(pk11_ctx->session);
+ if (cnt == 0)
+ DST_RET(ISC_R_NOTFOUND);
+ if (cnt > 1)
+ DST_RET(ISC_R_EXISTS);
+
+ if (engine != NULL) {
+ key->engine = isc_mem_strdup(key->mctx, engine);
+ 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);
+
+ pk11_return_session(pk11_ctx);
+ memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+ isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+ return (ISC_R_SUCCESS);
+
+ err:
+ if (pk11_ctx != NULL) {
+ pk11_return_session(pk11_ctx);
+ memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+ isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+ }
+ return (ret);
+}
+
+static isc_result_t
+pkcs11eddsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
+ dst_private_t priv;
+ isc_result_t ret;
+ pk11_object_t *ec = NULL;
+ CK_ATTRIBUTE *attr, *pattr;
+ isc_mem_t *mctx = key->mctx;
+ unsigned int i;
+ const char *engine = NULL, *label = NULL;
+
+ REQUIRE(key->key_alg == DST_ALG_ED25519 ||
+ key->key_alg == DST_ALG_ED448);
+
+ if ((pub == NULL) || (pub->keydata.pkey == NULL))
+ DST_RET(DST_R_INVALIDPRIVATEKEY);
+
+ /* read private key file */
+ ret = dst__privstruct_parse(key, DST_ALG_ED25519, lexer, mctx, &priv);
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+
+ if (key->external) {
+ if (priv.nelements != 0)
+ DST_RET(DST_R_INVALIDPRIVATEKEY);
+
+ key->keydata.pkey = pub->keydata.pkey;
+ pub->keydata.pkey = NULL;
+ key->key_size = pub->key_size;
+
+ dst__privstruct_free(&priv, mctx);
+ memset(&priv, 0, sizeof(priv));
+
+ return (ISC_R_SUCCESS);
+ }
+
+ for (i = 0; i < priv.nelements; i++) {
+ switch (priv.elements[i].tag) {
+ case TAG_EDDSA_ENGINE:
+ engine = (char *)priv.elements[i].data;
+ break;
+ case TAG_EDDSA_LABEL:
+ label = (char *)priv.elements[i].data;
+ break;
+ default:
+ break;
+ }
+ }
+ ec = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*ec));
+ if (ec == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(ec, 0, sizeof(*ec));
+ key->keydata.pkey = ec;
+
+ /* Is this key is stored in a HSM? See if we can fetch it. */
+ if ((label != NULL) || (engine != NULL)) {
+ ret = pkcs11eddsa_fetch(key, engine, label, pub);
+ if (ret != ISC_R_SUCCESS)
+ goto err;
+ dst__privstruct_free(&priv, mctx);
+ memset(&priv, 0, sizeof(priv));
+ return (ret);
+ }
+
+ ec->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 3);
+ if (ec->repr == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(ec->repr, 0, sizeof(*attr) * 3);
+ ec->attrcnt = 3;
+
+ attr = ec->repr;
+ attr->type = CKA_EC_PARAMS;
+ pattr = pk11_attribute_bytype(pub->keydata.pkey, CKA_EC_PARAMS);
+ INSIST(pattr != NULL);
+ attr->pValue = isc_mem_get(key->mctx, pattr->ulValueLen);
+ if (attr->pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(attr->pValue, pattr->pValue, pattr->ulValueLen);
+ attr->ulValueLen = pattr->ulValueLen;
+
+ attr++;
+ attr->type = CKA_EC_POINT;
+ pattr = pk11_attribute_bytype(pub->keydata.pkey, CKA_EC_POINT);
+ INSIST(pattr != NULL);
+ attr->pValue = isc_mem_get(key->mctx, pattr->ulValueLen);
+ if (attr->pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(attr->pValue, pattr->pValue, pattr->ulValueLen);
+ attr->ulValueLen = pattr->ulValueLen;
+
+ attr++;
+ attr->type = CKA_VALUE;
+ attr->pValue = isc_mem_get(key->mctx, priv.elements[0].length);
+ if (attr->pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(attr->pValue, priv.elements[0].data, priv.elements[0].length);
+ attr->ulValueLen = priv.elements[0].length;
+
+ dst__privstruct_free(&priv, mctx);
+ memset(&priv, 0, sizeof(priv));
+ if (key->key_alg == DST_ALG_ED25519)
+ key->key_size = DNS_KEY_ED25519SIZE;
+ else
+ key->key_size = DNS_KEY_ED448SIZE;
+
+ return (ISC_R_SUCCESS);
+
+ err:
+ pkcs11eddsa_destroy(key);
+ dst__privstruct_free(&priv, mctx);
+ memset(&priv, 0, sizeof(priv));
+ return (ret);
+}
+
+static isc_result_t
+pkcs11eddsa_fromlabel(dst_key_t *key, const char *engine, const char *label,
+ const char *pin)
+{
+ CK_RV rv;
+ CK_OBJECT_HANDLE hKey = CK_INVALID_HANDLE;
+ CK_OBJECT_CLASS keyClass = CKO_PUBLIC_KEY;
+ CK_KEY_TYPE keyType = CKK_EDDSA;
+ CK_ATTRIBUTE searchTemplate[] =
+ {
+ { CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+ { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+ { CKA_TOKEN, &truevalue, (CK_ULONG) sizeof(truevalue) },
+ { CKA_LABEL, NULL, 0 }
+ };
+ CK_ULONG cnt;
+ CK_ATTRIBUTE *attr;
+ pk11_object_t *ec;
+ pk11_context_t *pk11_ctx = NULL;
+ isc_result_t ret;
+ unsigned int i;
+
+ UNUSED(pin);
+
+ ec = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*ec));
+ if (ec == NULL)
+ return (ISC_R_NOMEMORY);
+ memset(ec, 0, sizeof(*ec));
+ ec->object = CK_INVALID_HANDLE;
+ ec->ontoken = true;
+ ec->reqlogon = true;
+ key->keydata.pkey = ec;
+
+ ec->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 2);
+ if (ec->repr == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(ec->repr, 0, sizeof(*attr) * 2);
+ ec->attrcnt = 2;
+ attr = ec->repr;
+ attr[0].type = CKA_EC_PARAMS;
+ attr[1].type = CKA_VALUE;
+
+ ret = pk11_parse_uri(ec, label, key->mctx, OP_EC);
+ if (ret != ISC_R_SUCCESS)
+ goto err;
+
+ pk11_ctx = (pk11_context_t *) isc_mem_get(key->mctx,
+ sizeof(*pk11_ctx));
+ if (pk11_ctx == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ ret = pk11_get_session(pk11_ctx, OP_EC, true, false,
+ ec->reqlogon, NULL, ec->slot);
+ if (ret != ISC_R_SUCCESS)
+ goto err;
+
+ attr = pk11_attribute_bytype(ec, CKA_LABEL);
+ if (attr == NULL) {
+ attr = pk11_attribute_bytype(ec, CKA_ID);
+ INSIST(attr != NULL);
+ searchTemplate[3].type = CKA_ID;
+ }
+ searchTemplate[3].pValue = attr->pValue;
+ searchTemplate[3].ulValueLen = attr->ulValueLen;
+
+ PK11_RET(pkcs_C_FindObjectsInit,
+ (pk11_ctx->session, searchTemplate, (CK_ULONG) 4),
+ DST_R_CRYPTOFAILURE);
+ PK11_RET(pkcs_C_FindObjects,
+ (pk11_ctx->session, &hKey, (CK_ULONG) 1, &cnt),
+ DST_R_CRYPTOFAILURE);
+ (void) pkcs_C_FindObjectsFinal(pk11_ctx->session);
+ if (cnt == 0)
+ DST_RET(ISC_R_NOTFOUND);
+ if (cnt > 1)
+ DST_RET(ISC_R_EXISTS);
+
+ attr = ec->repr;
+ PK11_RET(pkcs_C_GetAttributeValue,
+ (pk11_ctx->session, hKey, attr, 2),
+ DST_R_CRYPTOFAILURE);
+ for (i = 0; i <= 1; i++) {
+ attr[i].pValue = isc_mem_get(key->mctx, attr[i].ulValueLen);
+ if (attr[i].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(attr[i].pValue, 0, attr[i].ulValueLen);
+ }
+ PK11_RET(pkcs_C_GetAttributeValue,
+ (pk11_ctx->session, hKey, attr, 2),
+ DST_R_CRYPTOFAILURE);
+ attr[1].type = CKA_EC_POINT;
+
+ keyClass = CKO_PRIVATE_KEY;
+ PK11_RET(pkcs_C_FindObjectsInit,
+ (pk11_ctx->session, searchTemplate, (CK_ULONG) 4),
+ DST_R_CRYPTOFAILURE);
+ PK11_RET(pkcs_C_FindObjects,
+ (pk11_ctx->session, &ec->object, (CK_ULONG) 1, &cnt),
+ DST_R_CRYPTOFAILURE);
+ (void) pkcs_C_FindObjectsFinal(pk11_ctx->session);
+ if (cnt == 0)
+ DST_RET(ISC_R_NOTFOUND);
+ if (cnt > 1)
+ DST_RET(ISC_R_EXISTS);
+
+ if (engine != NULL) {
+ key->engine = isc_mem_strdup(key->mctx, engine);
+ 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);
+ if (key->key_alg == DST_ALG_ED25519)
+ key->key_size = DNS_KEY_ED25519SIZE;
+ else
+ key->key_size = DNS_KEY_ED448SIZE;
+
+ pk11_return_session(pk11_ctx);
+ memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+ isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+ return (ISC_R_SUCCESS);
+
+ err:
+ pkcs11eddsa_destroy(key);
+ if (pk11_ctx != NULL) {
+ pk11_return_session(pk11_ctx);
+ memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+ isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+ }
+ return (ret);
+}
+
+static dst_func_t pkcs11eddsa_functions = {
+ pkcs11eddsa_createctx,
+ NULL, /*%< createctx2 */
+ pkcs11eddsa_destroyctx,
+ pkcs11eddsa_adddata,
+ pkcs11eddsa_sign,
+ pkcs11eddsa_verify,
+ NULL, /*%< verify2 */
+ NULL, /*%< computesecret */
+ pkcs11eddsa_compare,
+ NULL, /*%< paramcompare */
+ pkcs11eddsa_generate,
+ pkcs11eddsa_isprivate,
+ pkcs11eddsa_destroy,
+ pkcs11eddsa_todns,
+ pkcs11eddsa_fromdns,
+ pkcs11eddsa_tofile,
+ pkcs11eddsa_parse,
+ NULL, /*%< cleanup */
+ pkcs11eddsa_fromlabel,
+ NULL, /*%< dump */
+ NULL, /*%< restore */
+};
+
+isc_result_t
+dst__pkcs11eddsa_init(dst_func_t **funcp) {
+ REQUIRE(funcp != NULL);
+ if (*funcp == NULL)
+ *funcp = &pkcs11eddsa_functions;
+ return (ISC_R_SUCCESS);
+}
+
+#else /* PKCS11CRYPTO && HAVE_PKCS11_EDxxx */
+
+#include <isc/util.h>
+
+EMPTY_TRANSLATION_UNIT
+
+#endif /* PKCS11CRYPTO && HAVE_PKCS11_EDxxx */
+/*! \file */
diff --git a/lib/dns/pkcs11gost_link.c b/lib/dns/pkcs11gost_link.c
new file mode 100644
index 0000000..b5c98f4
--- /dev/null
+++ b/lib/dns/pkcs11gost_link.c
@@ -0,0 +1,957 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <config.h>
+
+#if defined(PKCS11CRYPTO) && defined(HAVE_PKCS11_GOST)
+
+#include <stdbool.h>
+
+#include <isc/entropy.h>
+#include <isc/mem.h>
+#include <isc/safe.h>
+#include <isc/sha2.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dns/keyvalues.h>
+#include <dns/log.h>
+#include <dst/result.h>
+
+#include "dst_internal.h"
+#include "dst_parse.h"
+#include "dst_pkcs11.h"
+#include "dst_gost.h"
+
+#include <pk11/pk11.h>
+#include <pk11/internal.h>
+#define WANT_GOST_PARAMS
+#include <pk11/constants.h>
+
+#include <pkcs11/pkcs11.h>
+
+/*
+ * RU CryptoPro GOST keys:
+ * mechanisms:
+ * CKM_GOSTR3411
+ * CKM_GOSTR3410_WITH_GOSTR3411
+ * CKM_GOSTR3410_KEY_PAIR_GEN
+ * domain parameters:
+ * CKA_GOSTR3410_PARAMS (fixed BER OID 1.2.643.2.2.35.1)
+ * CKA_GOSTR3411_PARAMS (fixed BER OID 1.2.643.2.2.30.1)
+ * CKA_GOST28147_PARAMS (optional, don't use)
+ * public keys:
+ * object class CKO_PUBLIC_KEY
+ * key type CKK_GOSTR3410
+ * attribute CKA_VALUE (point Q)
+ * attribute CKA_GOSTR3410_PARAMS
+ * attribute CKA_GOSTR3411_PARAMS
+ * attribute CKA_GOST28147_PARAMS
+ * private keys:
+ * object class CKO_PRIVATE_KEY
+ * key type CKK_GOSTR3410
+ * attribute CKA_VALUE (big int d)
+ * attribute CKA_GOSTR3410_PARAMS
+ * attribute CKA_GOSTR3411_PARAMS
+ * attribute CKA_GOST28147_PARAMS
+ * point format: <x> <y> (little endian)
+ */
+
+#define CKA_VALUE2 CKA_PRIVATE_EXPONENT
+
+#define ISC_GOST_SIGNATURELENGTH 64
+#define ISC_GOST_PUBKEYLENGTH 64
+#define ISC_GOST_KEYSIZE 256
+
+/* HASH methods */
+
+isc_result_t
+isc_gost_init(isc_gost_t *ctx) {
+ CK_RV rv;
+ CK_MECHANISM mech = { CKM_GOSTR3411, NULL, 0 };
+ int ret = ISC_R_SUCCESS;
+
+ ret = pk11_get_session(ctx, OP_GOST, true, false,
+ false, NULL, 0);
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+ PK11_CALL(pkcs_C_DigestInit, (ctx->session, &mech), ISC_R_FAILURE);
+ return (ret);
+}
+
+void
+isc_gost_invalidate(isc_gost_t *ctx) {
+ CK_BYTE garbage[ISC_GOST_DIGESTLENGTH];
+ CK_ULONG len = ISC_GOST_DIGESTLENGTH;
+
+ if (ctx->handle == NULL)
+ return;
+ (void) pkcs_C_DigestFinal(ctx->session, garbage, &len);
+ isc_safe_memwipe(garbage, sizeof(garbage));
+ pk11_return_session(ctx);
+}
+
+isc_result_t
+isc_gost_update(isc_gost_t *ctx, const unsigned char *buf, unsigned int len) {
+ CK_RV rv;
+ CK_BYTE_PTR pPart;
+ int ret = ISC_R_SUCCESS;
+
+ DE_CONST(buf, pPart);
+ PK11_CALL(pkcs_C_DigestUpdate,
+ (ctx->session, pPart, (CK_ULONG) len),
+ ISC_R_FAILURE);
+ return (ret);
+}
+
+isc_result_t
+isc_gost_final(isc_gost_t *ctx, unsigned char *digest) {
+ CK_RV rv;
+ CK_ULONG len = ISC_GOST_DIGESTLENGTH;
+ int ret = ISC_R_SUCCESS;
+
+ PK11_CALL(pkcs_C_DigestFinal,
+ (ctx->session, (CK_BYTE_PTR) digest, &len),
+ ISC_R_FAILURE);
+ pk11_return_session(ctx);
+ return (ret);
+}
+
+/* DST methods */
+
+static CK_BBOOL truevalue = TRUE;
+static CK_BBOOL falsevalue = FALSE;
+
+#define DST_RET(a) {ret = a; goto err;}
+
+static isc_result_t pkcs11gost_todns(const dst_key_t *key, isc_buffer_t *data);
+static void pkcs11gost_destroy(dst_key_t *key);
+
+static isc_result_t
+pkcs11gost_createctx_sign(dst_key_t *key, dst_context_t *dctx) {
+ CK_RV rv;
+ CK_MECHANISM mech = { CKM_GOSTR3410_WITH_GOSTR3411, NULL, 0 };
+ CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY;
+ CK_KEY_TYPE keyType = CKK_GOSTR3410;
+ CK_ATTRIBUTE keyTemplate[] =
+ {
+ { CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+ { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+ { CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_SENSITIVE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_SIGN, &truevalue, (CK_ULONG) sizeof(truevalue) },
+ { CKA_VALUE, NULL, 0 },
+ { CKA_GOSTR3410_PARAMS, pk11_gost_a_paramset,
+ (CK_ULONG) sizeof(pk11_gost_a_paramset) },
+ { CKA_GOSTR3411_PARAMS, pk11_gost_paramset,
+ (CK_ULONG) sizeof(pk11_gost_paramset) }
+ };
+ CK_ATTRIBUTE *attr;
+ pk11_object_t *gost;
+ pk11_context_t *pk11_ctx;
+ isc_result_t ret;
+ unsigned int i;
+
+ REQUIRE(key != NULL);
+ gost = key->keydata.pkey;
+ REQUIRE(gost != NULL);
+
+ pk11_ctx = (pk11_context_t *) isc_mem_get(dctx->mctx,
+ sizeof(*pk11_ctx));
+ if (pk11_ctx == NULL)
+ return (ISC_R_NOMEMORY);
+ ret = pk11_get_session(pk11_ctx, OP_GOST, true, false,
+ gost->reqlogon, NULL,
+ pk11_get_best_token(OP_GOST));
+ if (ret != ISC_R_SUCCESS)
+ goto err;
+
+ if (gost->ontoken && (gost->object != CK_INVALID_HANDLE)) {
+ pk11_ctx->ontoken = gost->ontoken;
+ pk11_ctx->object = gost->object;
+ goto token_key;
+ }
+
+ for (attr = pk11_attribute_first(gost);
+ attr != NULL;
+ attr = pk11_attribute_next(gost, attr))
+ switch (attr->type) {
+ case CKA_VALUE2:
+ INSIST(keyTemplate[6].type == CKA_VALUE);
+ keyTemplate[6].pValue = isc_mem_get(dctx->mctx,
+ attr->ulValueLen);
+ if (keyTemplate[6].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[6].pValue, attr->pValue,
+ attr->ulValueLen);
+ keyTemplate[6].ulValueLen = attr->ulValueLen;
+ break;
+ }
+ pk11_ctx->object = CK_INVALID_HANDLE;
+ pk11_ctx->ontoken = false;
+ PK11_RET(pkcs_C_CreateObject,
+ (pk11_ctx->session,
+ keyTemplate, (CK_ULONG) 9,
+ &pk11_ctx->object),
+ ISC_R_FAILURE);
+
+ token_key:
+
+ PK11_RET(pkcs_C_SignInit,
+ (pk11_ctx->session, &mech, pk11_ctx->object),
+ ISC_R_FAILURE);
+
+ dctx->ctxdata.pk11_ctx = pk11_ctx;
+
+ for (i = 6; i <= 6; i++)
+ if (keyTemplate[i].pValue != NULL) {
+ isc_safe_memwipe(keyTemplate[i].pValue,
+ keyTemplate[i].ulValueLen);
+ isc_mem_put(dctx->mctx,
+ keyTemplate[i].pValue,
+ keyTemplate[i].ulValueLen);
+ }
+
+ return (ISC_R_SUCCESS);
+
+ err:
+ if (!pk11_ctx->ontoken && (pk11_ctx->object != CK_INVALID_HANDLE))
+ (void) pkcs_C_DestroyObject(pk11_ctx->session, pk11_ctx->object);
+ for (i = 6; i <= 6; i++)
+ if (keyTemplate[i].pValue != NULL) {
+ isc_safe_memwipe(keyTemplate[i].pValue,
+ keyTemplate[i].ulValueLen);
+ isc_mem_put(dctx->mctx,
+ keyTemplate[i].pValue,
+ keyTemplate[i].ulValueLen);
+ }
+ pk11_return_session(pk11_ctx);
+ isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx));
+ isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+ return (ret);
+}
+
+static isc_result_t
+pkcs11gost_createctx_verify(dst_key_t *key, dst_context_t *dctx) {
+ CK_RV rv;
+ CK_MECHANISM mech = { CKM_GOSTR3410_WITH_GOSTR3411, NULL, 0 };
+ CK_OBJECT_CLASS keyClass = CKO_PUBLIC_KEY;
+ CK_KEY_TYPE keyType = CKK_GOSTR3410;
+ CK_ATTRIBUTE keyTemplate[] =
+ {
+ { CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+ { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+ { CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_VERIFY, &truevalue, (CK_ULONG) sizeof(truevalue) },
+ { CKA_VALUE, NULL, 0 },
+ { CKA_GOSTR3410_PARAMS, pk11_gost_a_paramset,
+ (CK_ULONG) sizeof(pk11_gost_a_paramset) },
+ { CKA_GOSTR3411_PARAMS, pk11_gost_paramset,
+ (CK_ULONG) sizeof(pk11_gost_paramset) }
+ };
+ CK_ATTRIBUTE *attr;
+ pk11_object_t *gost;
+ pk11_context_t *pk11_ctx;
+ isc_result_t ret;
+ unsigned int i;
+
+ REQUIRE(key != NULL);
+ gost = key->keydata.pkey;
+ REQUIRE(gost != NULL);
+
+ pk11_ctx = (pk11_context_t *) isc_mem_get(dctx->mctx,
+ sizeof(*pk11_ctx));
+ if (pk11_ctx == NULL)
+ return (ISC_R_NOMEMORY);
+ ret = pk11_get_session(pk11_ctx, OP_GOST, true, false,
+ gost->reqlogon, NULL,
+ pk11_get_best_token(OP_GOST));
+ if (ret != ISC_R_SUCCESS)
+ goto err;
+
+ if (gost->ontoken && (gost->object != CK_INVALID_HANDLE)) {
+ pk11_ctx->ontoken = gost->ontoken;
+ pk11_ctx->object = gost->object;
+ goto token_key;
+ }
+
+ for (attr = pk11_attribute_first(gost);
+ attr != NULL;
+ attr = pk11_attribute_next(gost, attr))
+ switch (attr->type) {
+ case CKA_VALUE:
+ INSIST(keyTemplate[5].type == attr->type);
+ keyTemplate[5].pValue = isc_mem_get(dctx->mctx,
+ attr->ulValueLen);
+ if (keyTemplate[5].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[5].pValue, attr->pValue,
+ attr->ulValueLen);
+ keyTemplate[5].ulValueLen = attr->ulValueLen;
+ break;
+ }
+ pk11_ctx->object = CK_INVALID_HANDLE;
+ pk11_ctx->ontoken = false;
+ PK11_RET(pkcs_C_CreateObject,
+ (pk11_ctx->session,
+ keyTemplate, (CK_ULONG) 8,
+ &pk11_ctx->object),
+ ISC_R_FAILURE);
+
+ token_key:
+
+ PK11_RET(pkcs_C_VerifyInit,
+ (pk11_ctx->session, &mech, pk11_ctx->object),
+ ISC_R_FAILURE);
+
+ dctx->ctxdata.pk11_ctx = pk11_ctx;
+
+ for (i = 5; i <= 5; i++)
+ if (keyTemplate[i].pValue != NULL) {
+ isc_safe_memwipe(keyTemplate[i].pValue,
+ keyTemplate[i].ulValueLen);
+ isc_mem_put(dctx->mctx,
+ keyTemplate[i].pValue,
+ keyTemplate[i].ulValueLen);
+ }
+
+ return (ISC_R_SUCCESS);
+
+ err:
+ if (!pk11_ctx->ontoken && (pk11_ctx->object != CK_INVALID_HANDLE))
+ (void) pkcs_C_DestroyObject(pk11_ctx->session, pk11_ctx->object);
+ for (i = 5; i <= 5; i++)
+ if (keyTemplate[i].pValue != NULL) {
+ isc_safe_memwipe(keyTemplate[i].pValue,
+ keyTemplate[i].ulValueLen);
+ isc_mem_put(dctx->mctx,
+ keyTemplate[i].pValue,
+ keyTemplate[i].ulValueLen);
+ }
+ pk11_return_session(pk11_ctx);
+ isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx));
+ isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+ return (ret);
+}
+
+static isc_result_t
+pkcs11gost_createctx(dst_key_t *key, dst_context_t *dctx) {
+ if (dctx->use == DO_SIGN)
+ return (pkcs11gost_createctx_sign(key, dctx));
+ else
+ return (pkcs11gost_createctx_verify(key, dctx));
+}
+
+static void
+pkcs11gost_destroyctx(dst_context_t *dctx) {
+ pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx;
+
+ if (pk11_ctx != NULL) {
+ if (!pk11_ctx->ontoken &&
+ (pk11_ctx->object != CK_INVALID_HANDLE))
+ (void) pkcs_C_DestroyObject(pk11_ctx->session,
+ pk11_ctx->object);
+ pk11_return_session(pk11_ctx);
+ isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx));
+ isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx));
+ dctx->ctxdata.pk11_ctx = NULL;
+ }
+}
+
+static isc_result_t
+pkcs11gost_adddata(dst_context_t *dctx, const isc_region_t *data) {
+ CK_RV rv;
+ pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx;
+ isc_result_t ret = ISC_R_SUCCESS;
+
+ if (dctx->use == DO_SIGN)
+ PK11_CALL(pkcs_C_SignUpdate,
+ (pk11_ctx->session,
+ (CK_BYTE_PTR) data->base,
+ (CK_ULONG) data->length),
+ ISC_R_FAILURE);
+ else
+ PK11_CALL(pkcs_C_VerifyUpdate,
+ (pk11_ctx->session,
+ (CK_BYTE_PTR) data->base,
+ (CK_ULONG) data->length),
+ ISC_R_FAILURE);
+ return (ret);
+}
+
+static isc_result_t
+pkcs11gost_sign(dst_context_t *dctx, isc_buffer_t *sig) {
+ CK_RV rv;
+ CK_ULONG siglen = ISC_GOST_SIGNATURELENGTH;
+ isc_region_t r;
+ pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx;
+ isc_result_t ret = ISC_R_SUCCESS;
+
+ isc_buffer_availableregion(sig, &r);
+ if (r.length < ISC_GOST_SIGNATURELENGTH)
+ return (ISC_R_NOSPACE);
+
+ PK11_RET(pkcs_C_SignFinal,
+ (pk11_ctx->session, (CK_BYTE_PTR) r.base, &siglen),
+ DST_R_SIGNFAILURE);
+ if (siglen != ISC_GOST_SIGNATURELENGTH)
+ return (DST_R_SIGNFAILURE);
+
+ isc_buffer_add(sig, ISC_GOST_SIGNATURELENGTH);
+
+ err:
+ return (ret);
+}
+
+static isc_result_t
+pkcs11gost_verify(dst_context_t *dctx, const isc_region_t *sig) {
+ CK_RV rv;
+ pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx;
+ isc_result_t ret = ISC_R_SUCCESS;
+
+ PK11_CALL(pkcs_C_VerifyFinal,
+ (pk11_ctx->session,
+ (CK_BYTE_PTR) sig->base,
+ (CK_ULONG) sig->length),
+ DST_R_VERIFYFAILURE);
+ return (ret);
+}
+
+static bool
+pkcs11gost_compare(const dst_key_t *key1, const dst_key_t *key2) {
+ pk11_object_t *gost1, *gost2;
+ CK_ATTRIBUTE *attr1, *attr2;
+
+ gost1 = key1->keydata.pkey;
+ gost2 = key2->keydata.pkey;
+
+ if ((gost1 == NULL) && (gost2 == NULL))
+ return (true);
+ else if ((gost1 == NULL) || (gost2 == NULL))
+ return (false);
+
+ attr1 = pk11_attribute_bytype(gost1, CKA_VALUE);
+ attr2 = pk11_attribute_bytype(gost2, CKA_VALUE);
+ if ((attr1 == NULL) && (attr2 == NULL))
+ return (true);
+ else if ((attr1 == NULL) || (attr2 == NULL) ||
+ (attr1->ulValueLen != attr2->ulValueLen) ||
+ !isc_safe_memequal(attr1->pValue, attr2->pValue,
+ attr1->ulValueLen))
+ return (false);
+
+ attr1 = pk11_attribute_bytype(gost1, CKA_VALUE2);
+ attr2 = pk11_attribute_bytype(gost2, CKA_VALUE2);
+ if (((attr1 != NULL) || (attr2 != NULL)) &&
+ ((attr1 == NULL) || (attr2 == NULL) ||
+ (attr1->ulValueLen != attr2->ulValueLen) ||
+ !isc_safe_memequal(attr1->pValue, attr2->pValue,
+ attr1->ulValueLen)))
+ return (false);
+
+ if (!gost1->ontoken && !gost2->ontoken)
+ return (true);
+ else if (gost1->ontoken || gost2->ontoken ||
+ (gost1->object != gost2->object))
+ return (false);
+
+ return (true);
+}
+
+static isc_result_t
+pkcs11gost_generate(dst_key_t *key, int unused, void (*callback)(int)) {
+ CK_RV rv;
+ CK_MECHANISM mech = { CKM_GOSTR3410_KEY_PAIR_GEN, NULL, 0 };
+ CK_KEY_TYPE keyType = CKK_GOSTR3410;
+ CK_OBJECT_HANDLE pub = CK_INVALID_HANDLE;
+ CK_OBJECT_CLASS pubClass = CKO_PUBLIC_KEY;
+ CK_ATTRIBUTE pubTemplate[] =
+ {
+ { CKA_CLASS, &pubClass, (CK_ULONG) sizeof(pubClass) },
+ { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+ { CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_VERIFY, &truevalue, (CK_ULONG) sizeof(truevalue) },
+ { CKA_GOSTR3410_PARAMS, pk11_gost_a_paramset,
+ (CK_ULONG) sizeof(pk11_gost_a_paramset) },
+ { CKA_GOSTR3411_PARAMS, pk11_gost_paramset,
+ (CK_ULONG) sizeof(pk11_gost_paramset) }
+ };
+ CK_OBJECT_HANDLE priv = CK_INVALID_HANDLE;
+ CK_OBJECT_HANDLE privClass = CKO_PRIVATE_KEY;
+ CK_ATTRIBUTE privTemplate[] =
+ {
+ { CKA_CLASS, &privClass, (CK_ULONG) sizeof(privClass) },
+ { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+ { CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_SENSITIVE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_EXTRACTABLE, &truevalue, (CK_ULONG) sizeof(truevalue) },
+ { CKA_SIGN, &truevalue, (CK_ULONG) sizeof(truevalue) },
+ };
+ CK_ATTRIBUTE *attr;
+ pk11_object_t *gost;
+ pk11_context_t *pk11_ctx;
+ isc_result_t ret;
+
+ UNUSED(unused);
+ UNUSED(callback);
+
+ pk11_ctx = (pk11_context_t *) isc_mem_get(key->mctx,
+ sizeof(*pk11_ctx));
+ if (pk11_ctx == NULL)
+ return (ISC_R_NOMEMORY);
+ ret = pk11_get_session(pk11_ctx, OP_GOST, true, false,
+ false, NULL, pk11_get_best_token(OP_GOST));
+ if (ret != ISC_R_SUCCESS)
+ goto err;
+
+ PK11_RET(pkcs_C_GenerateKeyPair,
+ (pk11_ctx->session, &mech,
+ pubTemplate, (CK_ULONG) 7,
+ privTemplate, (CK_ULONG) 7,
+ &pub, &priv),
+ DST_R_CRYPTOFAILURE);
+
+ gost = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*gost));
+ if (gost == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(gost, 0, sizeof(*gost));
+ key->keydata.pkey = gost;
+ key->key_size = ISC_GOST_KEYSIZE;
+ gost->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx,
+ sizeof(*attr) * 2);
+ if (gost->repr == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(gost->repr, 0, sizeof(*attr) * 2);
+ gost->attrcnt = 2;
+
+ attr = gost->repr;
+ attr[0].type = CKA_VALUE;
+ attr[1].type = CKA_VALUE2;
+
+ attr = gost->repr;
+ PK11_RET(pkcs_C_GetAttributeValue,
+ (pk11_ctx->session, pub, attr, 1),
+ DST_R_CRYPTOFAILURE);
+ attr->pValue = isc_mem_get(key->mctx, attr->ulValueLen);
+ if (attr->pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(attr->pValue, 0, attr->ulValueLen);
+ PK11_RET(pkcs_C_GetAttributeValue,
+ (pk11_ctx->session, pub, attr, 1),
+ DST_R_CRYPTOFAILURE);
+
+ attr++;
+ attr->type = CKA_VALUE;
+ PK11_RET(pkcs_C_GetAttributeValue,
+ (pk11_ctx->session, priv, attr, 1),
+ DST_R_CRYPTOFAILURE);
+ attr->pValue = isc_mem_get(key->mctx, attr->ulValueLen);
+ if (attr->pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(attr->pValue, 0, attr->ulValueLen);
+ PK11_RET(pkcs_C_GetAttributeValue,
+ (pk11_ctx->session, priv, attr, 1),
+ DST_R_CRYPTOFAILURE);
+ attr->type = CKA_VALUE2;
+
+ (void) pkcs_C_DestroyObject(pk11_ctx->session, priv);
+ (void) pkcs_C_DestroyObject(pk11_ctx->session, pub);
+ pk11_return_session(pk11_ctx);
+ isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx));
+ isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+ return (ISC_R_SUCCESS);
+
+ err:
+ pkcs11gost_destroy(key);
+ if (priv != CK_INVALID_HANDLE)
+ (void) pkcs_C_DestroyObject(pk11_ctx->session, priv);
+ if (pub != CK_INVALID_HANDLE)
+ (void) pkcs_C_DestroyObject(pk11_ctx->session, pub);
+ pk11_return_session(pk11_ctx);
+ isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx));
+ isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+ return (ret);
+}
+
+static bool
+pkcs11gost_isprivate(const dst_key_t *key) {
+ pk11_object_t *gost = key->keydata.pkey;
+ CK_ATTRIBUTE *attr;
+
+ if (gost == NULL)
+ return (false);
+ attr = pk11_attribute_bytype(gost, CKA_VALUE2);
+ return (attr != NULL || gost->ontoken);
+}
+
+static void
+pkcs11gost_destroy(dst_key_t *key) {
+ pk11_object_t *gost = key->keydata.pkey;
+ CK_ATTRIBUTE *attr;
+
+ if (gost == NULL)
+ return;
+
+ INSIST((gost->object == CK_INVALID_HANDLE) || gost->ontoken);
+
+ for (attr = pk11_attribute_first(gost);
+ attr != NULL;
+ attr = pk11_attribute_next(gost, attr))
+ switch (attr->type) {
+ case CKA_VALUE:
+ case CKA_VALUE2:
+ if (attr->pValue != NULL) {
+ isc_safe_memwipe(attr->pValue,
+ attr->ulValueLen);
+ isc_mem_put(key->mctx,
+ attr->pValue,
+ attr->ulValueLen);
+ }
+ break;
+ }
+ if (gost->repr != NULL) {
+ isc_safe_memwipe(gost->repr, gost->attrcnt * sizeof(*attr));
+ isc_mem_put(key->mctx,
+ gost->repr, gost->attrcnt * sizeof(*attr));
+ }
+ isc_safe_memwipe(gost, sizeof(*gost));
+ isc_mem_put(key->mctx, gost, sizeof(*gost));
+ key->keydata.pkey = NULL;
+}
+
+static isc_result_t
+pkcs11gost_todns(const dst_key_t *key, isc_buffer_t *data) {
+ pk11_object_t *gost;
+ isc_region_t r;
+ CK_ATTRIBUTE *attr;
+
+ REQUIRE(key->keydata.pkey != NULL);
+
+ gost = key->keydata.pkey;
+ attr = pk11_attribute_bytype(gost, CKA_VALUE);
+ if ((attr == NULL) || (attr->ulValueLen != ISC_GOST_PUBKEYLENGTH))
+ return (ISC_R_FAILURE);
+
+ isc_buffer_availableregion(data, &r);
+ if (r.length < ISC_GOST_PUBKEYLENGTH)
+ return (ISC_R_NOSPACE);
+ memmove(r.base, (CK_BYTE_PTR) attr->pValue, ISC_GOST_PUBKEYLENGTH);
+ isc_buffer_add(data, ISC_GOST_PUBKEYLENGTH);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+pkcs11gost_fromdns(dst_key_t *key, isc_buffer_t *data) {
+ pk11_object_t *gost;
+ isc_region_t r;
+ CK_ATTRIBUTE *attr;
+
+ isc_buffer_remainingregion(data, &r);
+ if (r.length == 0)
+ return (ISC_R_SUCCESS);
+ if (r.length != ISC_GOST_PUBKEYLENGTH)
+ return (DST_R_INVALIDPUBLICKEY);
+
+ gost = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*gost));
+ if (gost == NULL)
+ return (ISC_R_NOMEMORY);
+ memset(gost, 0, sizeof(*gost));
+ gost->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr));
+ if (gost->repr == NULL)
+ goto nomemory;
+ gost->attrcnt = 1;
+
+ attr = gost->repr;
+ attr->type = CKA_VALUE;
+ attr->pValue = isc_mem_get(key->mctx, ISC_GOST_PUBKEYLENGTH);
+ if (attr->pValue == NULL)
+ goto nomemory;
+ memmove((CK_BYTE_PTR) attr->pValue, r.base, ISC_GOST_PUBKEYLENGTH);
+ attr->ulValueLen = ISC_GOST_PUBKEYLENGTH;
+
+ isc_buffer_forward(data, ISC_GOST_PUBKEYLENGTH);
+ key->keydata.pkey = gost;
+ key->key_size = ISC_GOST_KEYSIZE;
+ return (ISC_R_SUCCESS);
+
+ nomemory:
+ for (attr = pk11_attribute_first(gost);
+ attr != NULL;
+ attr = pk11_attribute_next(gost, attr))
+ switch (attr->type) {
+ case CKA_VALUE:
+ if (attr->pValue != NULL) {
+ isc_safe_memwipe(attr->pValue,
+ attr->ulValueLen);
+ isc_mem_put(key->mctx,
+ attr->pValue,
+ attr->ulValueLen);
+ }
+ break;
+ }
+ if (gost->repr != NULL) {
+ isc_safe_memwipe(gost->repr, gost->attrcnt * sizeof(*attr));
+ isc_mem_put(key->mctx,
+ gost->repr, gost->attrcnt * sizeof(*attr));
+ }
+ isc_safe_memwipe(gost, sizeof(*gost));
+ isc_mem_put(key->mctx, gost, sizeof(*gost));
+ return (ISC_R_NOMEMORY);
+}
+
+static unsigned char gost_private_der[39] = {
+ 0x30, 0x45, 0x02, 0x01, 0x00, 0x30, 0x1c, 0x06,
+ 0x06, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x13, 0x30,
+ 0x12, 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02,
+ 0x23, 0x01, 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02,
+ 0x02, 0x1e, 0x01, 0x04, 0x22, 0x02, 0x20
+};
+
+#ifdef PREFER_GOSTASN1
+
+static isc_result_t
+pkcs11gost_tofile(const dst_key_t *key, const char *directory) {
+ isc_result_t ret;
+ pk11_object_t *gost;
+ dst_private_t priv;
+ unsigned char *buf = NULL;
+ unsigned int i = 0;
+ CK_ATTRIBUTE *attr;
+ int adj;
+
+ if (key->keydata.pkey == NULL)
+ return (DST_R_NULLKEY);
+
+ if (key->external) {
+ priv.nelements = 0;
+ return (dst__privstruct_writefile(key, &priv, directory));
+ }
+
+ gost = key->keydata.pkey;
+ attr = pk11_attribute_bytype(gost, CKA_VALUE2);
+ if (attr != NULL) {
+ buf = isc_mem_get(key->mctx, attr->ulValueLen + 39);
+ if (buf == NULL)
+ return (ISC_R_NOMEMORY);
+ priv.elements[i].tag = TAG_GOST_PRIVASN1;
+ priv.elements[i].length =
+ (unsigned short) attr->ulValueLen + 39;
+ memmove(buf, gost_private_der, 39);
+ memmove(buf + 39, attr->pValue, attr->ulValueLen);
+ adj = (int) attr->ulValueLen - 32;
+ if (adj != 0) {
+ buf[1] += adj;
+ buf[36] += adj;
+ buf[38] += adj;
+ }
+ priv.elements[i].data = buf;
+ i++;
+ } else
+ return (DST_R_CRYPTOFAILURE);
+
+ priv.nelements = i;
+ ret = dst__privstruct_writefile(key, &priv, directory);
+
+ if (buf != NULL) {
+ isc_safe_memwipe(buf, attr->ulValueLen);
+ isc_mem_put(key->mctx, buf, attr->ulValueLen);
+ }
+ return (ret);
+}
+
+#else
+
+static isc_result_t
+pkcs11gost_tofile(const dst_key_t *key, const char *directory) {
+ isc_result_t ret;
+ pk11_object_t *gost;
+ dst_private_t priv;
+ unsigned char *buf = NULL;
+ unsigned int i = 0;
+ CK_ATTRIBUTE *attr;
+
+ if (key->keydata.pkey == NULL)
+ return (DST_R_NULLKEY);
+
+ if (key->external) {
+ priv.nelements = 0;
+ return (dst__privstruct_writefile(key, &priv, directory));
+ }
+
+ gost = key->keydata.pkey;
+ attr = pk11_attribute_bytype(gost, CKA_VALUE2);
+ if (attr != NULL) {
+ buf = isc_mem_get(key->mctx, attr->ulValueLen);
+ if (buf == NULL)
+ return (ISC_R_NOMEMORY);
+ priv.elements[i].tag = TAG_GOST_PRIVRAW;
+ priv.elements[i].length = (unsigned short) attr->ulValueLen;
+ memmove(buf, attr->pValue, attr->ulValueLen);
+ priv.elements[i].data = buf;
+ i++;
+ } else
+ return (DST_R_CRYPTOFAILURE);
+
+ priv.nelements = i;
+ ret = dst__privstruct_writefile(key, &priv, directory);
+
+ if (buf != NULL) {
+ isc_safe_memwipe(buf, attr->ulValueLen);
+ isc_mem_put(key->mctx, buf, attr->ulValueLen);
+ }
+ return (ret);
+}
+#endif
+
+static isc_result_t
+pkcs11gost_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
+ dst_private_t priv;
+ isc_result_t ret;
+ pk11_object_t *gost = NULL;
+ CK_ATTRIBUTE *attr, *pattr;
+ isc_mem_t *mctx = key->mctx;
+
+ if ((pub == NULL) || (pub->keydata.pkey == NULL))
+ DST_RET(DST_R_INVALIDPRIVATEKEY);
+
+ /* read private key file */
+ ret = dst__privstruct_parse(key, DST_ALG_ECDSA256, lexer, mctx, &priv);
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+
+ if (key->external) {
+ if (priv.nelements != 0)
+ DST_RET(DST_R_INVALIDPRIVATEKEY);
+
+ key->keydata.pkey = pub->keydata.pkey;
+ pub->keydata.pkey = NULL;
+ key->key_size = pub->key_size;
+
+ dst__privstruct_free(&priv, mctx);
+ isc_safe_memwipe(&priv, sizeof(priv));
+
+ return (ISC_R_SUCCESS);
+ }
+
+ if (priv.elements[0].tag == TAG_GOST_PRIVASN1) {
+ int adj = (int) priv.elements[0].length - (39 + 32);
+ unsigned char buf[39];
+
+ if ((adj > 0) || (adj < -31))
+ DST_RET(DST_R_INVALIDPRIVATEKEY);
+ memmove(buf, gost_private_der, 39);
+ if (adj != 0) {
+ buf[1] += adj;
+ buf[36] += adj;
+ buf[38] += adj;
+ }
+ if (!isc_safe_memequal(priv.elements[0].data, buf, 39))
+ DST_RET(DST_R_INVALIDPRIVATEKEY);
+ priv.elements[0].tag = TAG_GOST_PRIVRAW;
+ priv.elements[0].length -= 39;
+ memmove(priv.elements[0].data,
+ priv.elements[0].data + 39,
+ 32 + adj);
+ }
+
+ gost = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*gost));
+ if (gost == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(gost, 0, sizeof(*gost));
+ key->keydata.pkey = gost;
+ key->key_size = ISC_GOST_KEYSIZE;
+
+ gost->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx,
+ sizeof(*attr) * 2);
+ if (gost->repr == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(gost->repr, 0, sizeof(*attr) * 2);
+ gost->attrcnt = 2;
+
+ attr = gost->repr;
+ attr->type = CKA_VALUE;
+ pattr = pk11_attribute_bytype(pub->keydata.pkey, CKA_VALUE);
+ INSIST(pattr != NULL);
+ attr->pValue = isc_mem_get(key->mctx, pattr->ulValueLen);
+ if (attr->pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(attr->pValue, pattr->pValue, pattr->ulValueLen);
+ attr->ulValueLen = pattr->ulValueLen;
+
+ attr++;
+ attr->type = CKA_VALUE2;
+ attr->pValue = isc_mem_get(key->mctx, priv.elements[0].length);
+ if (attr->pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(attr->pValue, priv.elements[0].data, priv.elements[0].length);
+ attr->ulValueLen = priv.elements[0].length;
+
+ dst__privstruct_free(&priv, mctx);
+ isc_safe_memwipe(&priv, sizeof(priv));
+
+ return (ISC_R_SUCCESS);
+
+ err:
+ pkcs11gost_destroy(key);
+ dst__privstruct_free(&priv, mctx);
+ isc_safe_memwipe(&priv, sizeof(priv));
+ return (ret);
+}
+
+static dst_func_t pkcs11gost_functions = {
+ pkcs11gost_createctx,
+ NULL, /*%< createctx2 */
+ pkcs11gost_destroyctx,
+ pkcs11gost_adddata,
+ pkcs11gost_sign,
+ pkcs11gost_verify,
+ NULL, /*%< verify2 */
+ NULL, /*%< computesecret */
+ pkcs11gost_compare,
+ NULL, /*%< paramcompare */
+ pkcs11gost_generate,
+ pkcs11gost_isprivate,
+ pkcs11gost_destroy,
+ pkcs11gost_todns,
+ pkcs11gost_fromdns,
+ pkcs11gost_tofile,
+ pkcs11gost_parse,
+ NULL, /*%< cleanup */
+ NULL, /*%< fromlabel */
+ NULL, /*%< dump */
+ NULL, /*%< restore */
+};
+
+isc_result_t
+dst__pkcs11gost_init(dst_func_t **funcp) {
+ REQUIRE(funcp != NULL);
+ if (*funcp == NULL)
+ *funcp = &pkcs11gost_functions;
+ return (ISC_R_SUCCESS);
+}
+
+#else /* PKCS11CRYPTO && HAVE_PKCS11_GOST */
+
+#include <isc/util.h>
+
+EMPTY_TRANSLATION_UNIT
+
+#endif /* PKCS11CRYPTO && HAVE_PKCS11_GOST */
+/*! \file */
diff --git a/lib/dns/pkcs11rsa_link.c b/lib/dns/pkcs11rsa_link.c
new file mode 100644
index 0000000..eb782c8
--- /dev/null
+++ b/lib/dns/pkcs11rsa_link.c
@@ -0,0 +1,2237 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifdef PKCS11CRYPTO
+
+#include <config.h>
+
+#include <stdbool.h>
+
+#include <isc/entropy.h>
+#include <isc/md5.h>
+#include <isc/sha1.h>
+#include <isc/sha2.h>
+#include <isc/mem.h>
+#include <isc/safe.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dst/result.h>
+
+#include "dst_internal.h"
+#include "dst_parse.h"
+#include "dst_pkcs11.h"
+
+#include <pk11/internal.h>
+#include <pk11/site.h>
+
+/*
+ * Limit the size of public exponents.
+ */
+#ifndef RSA_MAX_PUBEXP_BITS
+#define RSA_MAX_PUBEXP_BITS 35
+#endif
+
+#define DST_RET(a) {ret = a; goto err;}
+
+static CK_BBOOL truevalue = TRUE;
+static CK_BBOOL falsevalue = FALSE;
+
+static isc_result_t pkcs11rsa_todns(const dst_key_t *key, isc_buffer_t *data);
+static void pkcs11rsa_destroy(dst_key_t *key);
+static isc_result_t pkcs11rsa_fetch(dst_key_t *key, const char *engine,
+ const char *label, dst_key_t *pub);
+
+#ifndef PK11_RSA_PKCS_REPLACE
+
+static isc_result_t
+pkcs11rsa_createctx_sign(dst_key_t *key, dst_context_t *dctx) {
+ CK_RV rv;
+ CK_MECHANISM mech = { 0, NULL, 0 };
+ CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY;
+ CK_KEY_TYPE keyType = CKK_RSA;
+ CK_ATTRIBUTE keyTemplate[] =
+ {
+ { CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+ { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+ { CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_SENSITIVE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_SIGN, &truevalue, (CK_ULONG) sizeof(truevalue) },
+ { CKA_MODULUS, NULL, 0 },
+ { CKA_PUBLIC_EXPONENT, NULL, 0 },
+ { CKA_PRIVATE_EXPONENT, NULL, 0 },
+ { CKA_PRIME_1, NULL, 0 },
+ { CKA_PRIME_2, NULL, 0 },
+ { CKA_EXPONENT_1, NULL, 0 },
+ { CKA_EXPONENT_2, NULL, 0 },
+ { CKA_COEFFICIENT, NULL, 0 }
+ };
+ CK_ATTRIBUTE *attr;
+ CK_SLOT_ID slotid;
+ pk11_object_t *rsa;
+ pk11_context_t *pk11_ctx;
+ isc_result_t ret;
+ unsigned int i;
+
+#ifndef PK11_MD5_DISABLE
+ REQUIRE(key->key_alg == DST_ALG_RSAMD5 ||
+ key->key_alg == DST_ALG_RSASHA1 ||
+ key->key_alg == DST_ALG_NSEC3RSASHA1 ||
+ key->key_alg == DST_ALG_RSASHA256 ||
+ key->key_alg == DST_ALG_RSASHA512);
+#else
+ REQUIRE(key->key_alg == DST_ALG_RSASHA1 ||
+ key->key_alg == DST_ALG_NSEC3RSASHA1 ||
+ key->key_alg == DST_ALG_RSASHA256 ||
+ key->key_alg == DST_ALG_RSASHA512);
+#endif
+
+ /*
+ * Reject incorrect RSA key lengths.
+ */
+ switch (dctx->key->key_alg) {
+ case DST_ALG_RSAMD5:
+ case DST_ALG_RSASHA1:
+ case DST_ALG_NSEC3RSASHA1:
+ /* From RFC 3110 */
+ if (dctx->key->key_size > 4096)
+ return (ISC_R_FAILURE);
+ break;
+ case DST_ALG_RSASHA256:
+ /* From RFC 5702 */
+ if ((dctx->key->key_size < 512) ||
+ (dctx->key->key_size > 4096))
+ return (ISC_R_FAILURE);
+ break;
+ case DST_ALG_RSASHA512:
+ /* From RFC 5702 */
+ if ((dctx->key->key_size < 1024) ||
+ (dctx->key->key_size > 4096))
+ return (ISC_R_FAILURE);
+ break;
+ default:
+ INSIST(0);
+ }
+
+ rsa = key->keydata.pkey;
+
+ pk11_ctx = (pk11_context_t *) isc_mem_get(dctx->mctx,
+ sizeof(*pk11_ctx));
+ if (pk11_ctx == NULL)
+ return (ISC_R_NOMEMORY);
+ memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+ if (rsa->ontoken)
+ slotid = rsa->slot;
+ else
+ slotid = pk11_get_best_token(OP_RSA);
+ ret = pk11_get_session(pk11_ctx, OP_RSA, true, false,
+ rsa->reqlogon, NULL, slotid);
+ if (ret != ISC_R_SUCCESS)
+ goto err;
+
+ if (rsa->ontoken && (rsa->object != CK_INVALID_HANDLE)) {
+ pk11_ctx->ontoken = rsa->ontoken;
+ pk11_ctx->object = rsa->object;
+ goto token_key;
+ }
+
+ for (attr = pk11_attribute_first(rsa);
+ attr != NULL;
+ attr = pk11_attribute_next(rsa, attr))
+ switch (attr->type) {
+ case CKA_MODULUS:
+ INSIST(keyTemplate[6].type == attr->type);
+ keyTemplate[6].pValue = isc_mem_get(dctx->mctx,
+ attr->ulValueLen);
+ if (keyTemplate[6].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[6].pValue, attr->pValue,
+ attr->ulValueLen);
+ keyTemplate[6].ulValueLen = attr->ulValueLen;
+ break;
+ case CKA_PUBLIC_EXPONENT:
+ INSIST(keyTemplate[7].type == attr->type);
+ keyTemplate[7].pValue = isc_mem_get(dctx->mctx,
+ attr->ulValueLen);
+ if (keyTemplate[7].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[7].pValue, attr->pValue,
+ attr->ulValueLen);
+ keyTemplate[7].ulValueLen = attr->ulValueLen;
+ break;
+ case CKA_PRIVATE_EXPONENT:
+ INSIST(keyTemplate[8].type == attr->type);
+ keyTemplate[8].pValue = isc_mem_get(dctx->mctx,
+ attr->ulValueLen);
+ if (keyTemplate[8].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[8].pValue, attr->pValue,
+ attr->ulValueLen);
+ keyTemplate[8].ulValueLen = attr->ulValueLen;
+ break;
+ case CKA_PRIME_1:
+ INSIST(keyTemplate[9].type == attr->type);
+ keyTemplate[9].pValue = isc_mem_get(dctx->mctx,
+ attr->ulValueLen);
+ if (keyTemplate[9].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[9].pValue, attr->pValue,
+ attr->ulValueLen);
+ keyTemplate[9].ulValueLen = attr->ulValueLen;
+ break;
+ case CKA_PRIME_2:
+ INSIST(keyTemplate[10].type == attr->type);
+ keyTemplate[10].pValue = isc_mem_get(dctx->mctx,
+ attr->ulValueLen);
+ if (keyTemplate[10].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[10].pValue, attr->pValue,
+ attr->ulValueLen);
+ keyTemplate[10].ulValueLen = attr->ulValueLen;
+ break;
+ case CKA_EXPONENT_1:
+ INSIST(keyTemplate[11].type == attr->type);
+ keyTemplate[11].pValue = isc_mem_get(dctx->mctx,
+ attr->ulValueLen);
+ if (keyTemplate[11].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[11].pValue, attr->pValue,
+ attr->ulValueLen);
+ keyTemplate[11].ulValueLen = attr->ulValueLen;
+ break;
+ case CKA_EXPONENT_2:
+ INSIST(keyTemplate[12].type == attr->type);
+ keyTemplate[12].pValue = isc_mem_get(dctx->mctx,
+ attr->ulValueLen);
+ if (keyTemplate[12].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[12].pValue, attr->pValue,
+ attr->ulValueLen);
+ keyTemplate[12].ulValueLen = attr->ulValueLen;
+ break;
+ case CKA_COEFFICIENT:
+ INSIST(keyTemplate[13].type == attr->type);
+ keyTemplate[13].pValue = isc_mem_get(dctx->mctx,
+ attr->ulValueLen);
+ if (keyTemplate[13].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[13].pValue, attr->pValue,
+ attr->ulValueLen);
+ keyTemplate[13].ulValueLen = attr->ulValueLen;
+ break;
+ }
+ pk11_ctx->object = CK_INVALID_HANDLE;
+ pk11_ctx->ontoken = false;
+ PK11_RET(pkcs_C_CreateObject,
+ (pk11_ctx->session,
+ keyTemplate, (CK_ULONG) 14,
+ &pk11_ctx->object),
+ ISC_R_FAILURE);
+
+ token_key:
+
+ switch (dctx->key->key_alg) {
+#ifndef PK11_MD5_DISABLE
+ case DST_ALG_RSAMD5:
+ mech.mechanism = CKM_MD5_RSA_PKCS;
+ break;
+#endif
+ case DST_ALG_RSASHA1:
+ case DST_ALG_NSEC3RSASHA1:
+ mech.mechanism = CKM_SHA1_RSA_PKCS;
+ break;
+ case DST_ALG_RSASHA256:
+ mech.mechanism = CKM_SHA256_RSA_PKCS;
+ break;
+ case DST_ALG_RSASHA512:
+ mech.mechanism = CKM_SHA512_RSA_PKCS;
+ break;
+ default:
+ INSIST(0);
+ }
+
+ PK11_RET(pkcs_C_SignInit,
+ (pk11_ctx->session, &mech, pk11_ctx->object),
+ ISC_R_FAILURE);
+
+ dctx->ctxdata.pk11_ctx = pk11_ctx;
+
+ for (i = 6; i <= 13; i++)
+ if (keyTemplate[i].pValue != NULL) {
+ isc_safe_memwipe(keyTemplate[i].pValue,
+ keyTemplate[i].ulValueLen);
+ isc_mem_put(dctx->mctx,
+ keyTemplate[i].pValue,
+ keyTemplate[i].ulValueLen);
+ }
+
+ return (ISC_R_SUCCESS);
+
+ err:
+ if (!pk11_ctx->ontoken && (pk11_ctx->object != CK_INVALID_HANDLE))
+ (void) pkcs_C_DestroyObject(pk11_ctx->session,
+ pk11_ctx->object);
+ for (i = 6; i <= 13; i++)
+ if (keyTemplate[i].pValue != NULL) {
+ isc_safe_memwipe(keyTemplate[i].pValue,
+ keyTemplate[i].ulValueLen);
+ isc_mem_put(dctx->mctx,
+ keyTemplate[i].pValue,
+ keyTemplate[i].ulValueLen);
+ }
+ pk11_return_session(pk11_ctx);
+ isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx));
+ isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+ return (ret);
+}
+
+static isc_result_t
+pkcs11rsa_createctx_verify(dst_key_t *key, unsigned int maxbits,
+ dst_context_t *dctx) {
+ CK_RV rv;
+ CK_MECHANISM mech = { 0, NULL, 0 };
+ CK_OBJECT_CLASS keyClass = CKO_PUBLIC_KEY;
+ CK_KEY_TYPE keyType = CKK_RSA;
+ CK_ATTRIBUTE keyTemplate[] =
+ {
+ { CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+ { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+ { CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_VERIFY, &truevalue, (CK_ULONG) sizeof(truevalue) },
+ { CKA_MODULUS, NULL, 0 },
+ { CKA_PUBLIC_EXPONENT, NULL, 0 },
+ };
+ CK_ATTRIBUTE *attr;
+ pk11_object_t *rsa;
+ pk11_context_t *pk11_ctx;
+ isc_result_t ret;
+ unsigned int i;
+
+#ifndef PK11_MD5_DISABLE
+ REQUIRE(key->key_alg == DST_ALG_RSAMD5 ||
+ key->key_alg == DST_ALG_RSASHA1 ||
+ key->key_alg == DST_ALG_NSEC3RSASHA1 ||
+ key->key_alg == DST_ALG_RSASHA256 ||
+ key->key_alg == DST_ALG_RSASHA512);
+#else
+ REQUIRE(key->key_alg == DST_ALG_RSASHA1 ||
+ key->key_alg == DST_ALG_NSEC3RSASHA1 ||
+ key->key_alg == DST_ALG_RSASHA256 ||
+ key->key_alg == DST_ALG_RSASHA512);
+#endif
+
+ /*
+ * Reject incorrect RSA key lengths.
+ */
+ switch (dctx->key->key_alg) {
+ case DST_ALG_RSAMD5:
+ case DST_ALG_RSASHA1:
+ case DST_ALG_NSEC3RSASHA1:
+ /* From RFC 3110 */
+ if (dctx->key->key_size > 4096)
+ return (ISC_R_FAILURE);
+ break;
+ case DST_ALG_RSASHA256:
+ /* From RFC 5702 */
+ if ((dctx->key->key_size < 512) ||
+ (dctx->key->key_size > 4096))
+ return (ISC_R_FAILURE);
+ break;
+ case DST_ALG_RSASHA512:
+ /* From RFC 5702 */
+ if ((dctx->key->key_size < 1024) ||
+ (dctx->key->key_size > 4096))
+ return (ISC_R_FAILURE);
+ break;
+ default:
+ INSIST(0);
+ }
+
+ rsa = key->keydata.pkey;
+
+ pk11_ctx = (pk11_context_t *) isc_mem_get(dctx->mctx,
+ sizeof(*pk11_ctx));
+ if (pk11_ctx == NULL)
+ return (ISC_R_NOMEMORY);
+ ret = pk11_get_session(pk11_ctx, OP_RSA, true, false,
+ rsa->reqlogon, NULL,
+ pk11_get_best_token(OP_RSA));
+ if (ret != ISC_R_SUCCESS)
+ goto err;
+
+ for (attr = pk11_attribute_first(rsa);
+ attr != NULL;
+ attr = pk11_attribute_next(rsa, attr))
+ switch (attr->type) {
+ case CKA_MODULUS:
+ INSIST(keyTemplate[5].type == attr->type);
+ keyTemplate[5].pValue = isc_mem_get(dctx->mctx,
+ attr->ulValueLen);
+ if (keyTemplate[5].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[5].pValue, attr->pValue,
+ attr->ulValueLen);
+ keyTemplate[5].ulValueLen = attr->ulValueLen;
+ break;
+ case CKA_PUBLIC_EXPONENT:
+ INSIST(keyTemplate[6].type == attr->type);
+ keyTemplate[6].pValue = isc_mem_get(dctx->mctx,
+ attr->ulValueLen);
+ if (keyTemplate[6].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[6].pValue, attr->pValue,
+ attr->ulValueLen);
+ keyTemplate[6].ulValueLen = attr->ulValueLen;
+ if (pk11_numbits(attr->pValue,
+ attr->ulValueLen) > maxbits &&
+ maxbits != 0)
+ DST_RET(DST_R_VERIFYFAILURE);
+ break;
+ }
+ pk11_ctx->object = CK_INVALID_HANDLE;
+ pk11_ctx->ontoken = false;
+ PK11_RET(pkcs_C_CreateObject,
+ (pk11_ctx->session,
+ keyTemplate, (CK_ULONG) 7,
+ &pk11_ctx->object),
+ ISC_R_FAILURE);
+
+ switch (dctx->key->key_alg) {
+#ifndef PK11_MD5_DISABLE
+ case DST_ALG_RSAMD5:
+ mech.mechanism = CKM_MD5_RSA_PKCS;
+ break;
+#endif
+ case DST_ALG_RSASHA1:
+ case DST_ALG_NSEC3RSASHA1:
+ mech.mechanism = CKM_SHA1_RSA_PKCS;
+ break;
+ case DST_ALG_RSASHA256:
+ mech.mechanism = CKM_SHA256_RSA_PKCS;
+ break;
+ case DST_ALG_RSASHA512:
+ mech.mechanism = CKM_SHA512_RSA_PKCS;
+ break;
+ default:
+ INSIST(0);
+ }
+
+ PK11_RET(pkcs_C_VerifyInit,
+ (pk11_ctx->session, &mech, pk11_ctx->object),
+ ISC_R_FAILURE);
+
+ dctx->ctxdata.pk11_ctx = pk11_ctx;
+
+ for (i = 5; i <= 6; i++)
+ if (keyTemplate[i].pValue != NULL) {
+ isc_safe_memwipe(keyTemplate[i].pValue,
+ keyTemplate[i].ulValueLen);
+ isc_mem_put(dctx->mctx,
+ keyTemplate[i].pValue,
+ keyTemplate[i].ulValueLen);
+ }
+
+ return (ISC_R_SUCCESS);
+
+ err:
+ if (!pk11_ctx->ontoken && (pk11_ctx->object != CK_INVALID_HANDLE))
+ (void) pkcs_C_DestroyObject(pk11_ctx->session,
+ pk11_ctx->object);
+ for (i = 5; i <= 6; i++)
+ if (keyTemplate[i].pValue != NULL) {
+ isc_safe_memwipe(keyTemplate[i].pValue,
+ keyTemplate[i].ulValueLen);
+ isc_mem_put(dctx->mctx,
+ keyTemplate[i].pValue,
+ keyTemplate[i].ulValueLen);
+ }
+ pk11_return_session(pk11_ctx);
+ isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx));
+ isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+ return (ret);
+}
+
+static isc_result_t
+pkcs11rsa_createctx(dst_key_t *key, dst_context_t *dctx) {
+ if (dctx->use == DO_SIGN)
+ return (pkcs11rsa_createctx_sign(key, dctx));
+ else
+ return (pkcs11rsa_createctx_verify(key, 0U, dctx));
+}
+
+static isc_result_t
+pkcs11rsa_createctx2(dst_key_t *key, int maxbits, dst_context_t *dctx) {
+ if (dctx->use == DO_SIGN)
+ return (pkcs11rsa_createctx_sign(key, dctx));
+ else
+ return (pkcs11rsa_createctx_verify(key,
+ (unsigned) maxbits, dctx));
+}
+
+static void
+pkcs11rsa_destroyctx(dst_context_t *dctx) {
+ pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx;
+
+ if (pk11_ctx != NULL) {
+ if (!pk11_ctx->ontoken &&
+ (pk11_ctx->object != CK_INVALID_HANDLE))
+ (void) pkcs_C_DestroyObject(pk11_ctx->session,
+ pk11_ctx->object);
+ pk11_return_session(pk11_ctx);
+ isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx));
+ isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx));
+ dctx->ctxdata.pk11_ctx = NULL;
+ }
+}
+
+static isc_result_t
+pkcs11rsa_adddata(dst_context_t *dctx, const isc_region_t *data) {
+ CK_RV rv;
+ pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx;
+ isc_result_t ret = ISC_R_SUCCESS;
+
+ if (dctx->use == DO_SIGN)
+ PK11_CALL(pkcs_C_SignUpdate,
+ (pk11_ctx->session,
+ (CK_BYTE_PTR) data->base,
+ (CK_ULONG) data->length),
+ ISC_R_FAILURE);
+ else
+ PK11_CALL(pkcs_C_VerifyUpdate,
+ (pk11_ctx->session,
+ (CK_BYTE_PTR) data->base,
+ (CK_ULONG) data->length),
+ ISC_R_FAILURE);
+ return (ret);
+}
+
+static isc_result_t
+pkcs11rsa_sign(dst_context_t *dctx, isc_buffer_t *sig) {
+ CK_RV rv;
+ CK_ULONG siglen = 0;
+ isc_region_t r;
+ pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx;
+ isc_result_t ret = ISC_R_SUCCESS;
+
+ PK11_RET(pkcs_C_SignFinal,
+ (pk11_ctx->session, NULL, &siglen),
+ DST_R_SIGNFAILURE);
+
+ isc_buffer_availableregion(sig, &r);
+
+ if (r.length < (unsigned int) siglen)
+ return (ISC_R_NOSPACE);
+
+ PK11_RET(pkcs_C_SignFinal,
+ (pk11_ctx->session, (CK_BYTE_PTR) r.base, &siglen),
+ DST_R_SIGNFAILURE);
+
+ isc_buffer_add(sig, (unsigned int) siglen);
+
+ err:
+ return (ret);
+}
+
+static isc_result_t
+pkcs11rsa_verify(dst_context_t *dctx, const isc_region_t *sig) {
+ CK_RV rv;
+ pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx;
+ isc_result_t ret = ISC_R_SUCCESS;
+
+ PK11_CALL(pkcs_C_VerifyFinal,
+ (pk11_ctx->session,
+ (CK_BYTE_PTR) sig->base,
+ (CK_ULONG) sig->length),
+ DST_R_VERIFYFAILURE);
+ return (ret);
+}
+
+#else
+
+/*
+ * CKM_<hash>_RSA_PKCS mechanisms are not available so fall back
+ * to CKM_RSA_PKCS and do the EMSA-PKCS#1-v1.5 encapsulation by hand.
+ */
+
+CK_BYTE md5_der[] =
+ { 0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00,
+ 0x04, 0x10 };
+CK_BYTE sha1_der[] =
+ { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e,
+ 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 };
+CK_BYTE sha256_der[] =
+ { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
+ 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
+ 0x00, 0x04, 0x20 };
+CK_BYTE sha512_der[] =
+ { 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
+ 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
+ 0x00, 0x04, 0x40 };
+#define MAX_DER_SIZE 19
+#define MIN_PKCS1_PADLEN 11
+
+static isc_result_t
+pkcs11rsa_createctx(dst_key_t *key, dst_context_t *dctx) {
+ CK_RV rv;
+ CK_MECHANISM mech = { 0, NULL, 0 };
+ CK_SLOT_ID slotid;
+ pk11_object_t *rsa = key->keydata.pkey;
+ pk11_context_t *pk11_ctx;
+ isc_result_t ret;
+
+#ifndef PK11_MD5_DISABLE
+ REQUIRE(key->key_alg == DST_ALG_RSAMD5 ||
+ key->key_alg == DST_ALG_RSASHA1 ||
+ key->key_alg == DST_ALG_NSEC3RSASHA1 ||
+ key->key_alg == DST_ALG_RSASHA256 ||
+ key->key_alg == DST_ALG_RSASHA512);
+#else
+ REQUIRE(key->key_alg == DST_ALG_RSASHA1 ||
+ key->key_alg == DST_ALG_NSEC3RSASHA1 ||
+ key->key_alg == DST_ALG_RSASHA256 ||
+ key->key_alg == DST_ALG_RSASHA512);
+#endif
+ REQUIRE(rsa != NULL);
+
+ /*
+ * Reject incorrect RSA key lengths.
+ */
+ switch (dctx->key->key_alg) {
+ case DST_ALG_RSAMD5:
+ case DST_ALG_RSASHA1:
+ case DST_ALG_NSEC3RSASHA1:
+ /* From RFC 3110 */
+ if (dctx->key->key_size > 4096)
+ return (ISC_R_FAILURE);
+ break;
+ case DST_ALG_RSASHA256:
+ /* From RFC 5702 */
+ if ((dctx->key->key_size < 512) ||
+ (dctx->key->key_size > 4096))
+ return (ISC_R_FAILURE);
+ break;
+ case DST_ALG_RSASHA512:
+ /* From RFC 5702 */
+ if ((dctx->key->key_size < 1024) ||
+ (dctx->key->key_size > 4096))
+ return (ISC_R_FAILURE);
+ break;
+ default:
+ INSIST(0);
+ }
+
+ switch (key->key_alg) {
+#ifndef PK11_MD5_DISABLE
+ case DST_ALG_RSAMD5:
+ mech.mechanism = CKM_MD5;
+ break;
+#endif
+ case DST_ALG_RSASHA1:
+ case DST_ALG_NSEC3RSASHA1:
+ mech.mechanism = CKM_SHA_1;
+ break;
+ case DST_ALG_RSASHA256:
+ mech.mechanism = CKM_SHA256;
+ break;
+ case DST_ALG_RSASHA512:
+ mech.mechanism = CKM_SHA512;
+ break;
+ default:
+ INSIST(0);
+ }
+
+ pk11_ctx = (pk11_context_t *) isc_mem_get(dctx->mctx,
+ sizeof(*pk11_ctx));
+ if (pk11_ctx == NULL)
+ return (ISC_R_NOMEMORY);
+ memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+ if (rsa->ontoken)
+ slotid = rsa->slot;
+ else
+ slotid = pk11_get_best_token(OP_RSA);
+ ret = pk11_get_session(pk11_ctx, OP_RSA, true, false,
+ rsa->reqlogon, NULL, slotid);
+ if (ret != ISC_R_SUCCESS)
+ goto err;
+
+ PK11_RET(pkcs_C_DigestInit, (pk11_ctx->session, &mech), ISC_R_FAILURE);
+ dctx->ctxdata.pk11_ctx = pk11_ctx;
+ return (ISC_R_SUCCESS);
+
+ err:
+ pk11_return_session(pk11_ctx);
+ isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx));
+ isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+ return (ret);
+}
+
+static void
+pkcs11rsa_destroyctx(dst_context_t *dctx) {
+ CK_BYTE garbage[ISC_SHA512_DIGESTLENGTH];
+ CK_ULONG len = ISC_SHA512_DIGESTLENGTH;
+ pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx;
+
+ if (pk11_ctx != NULL) {
+ (void) pkcs_C_DigestFinal(pk11_ctx->session, garbage, &len);
+ isc_safe_memwipe(garbage, sizeof(garbage));
+ pk11_return_session(pk11_ctx);
+ isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx));
+ isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx));
+ dctx->ctxdata.pk11_ctx = NULL;
+ }
+}
+
+static isc_result_t
+pkcs11rsa_adddata(dst_context_t *dctx, const isc_region_t *data) {
+ CK_RV rv;
+ pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx;
+ isc_result_t ret = ISC_R_SUCCESS;
+
+ PK11_CALL(pkcs_C_DigestUpdate,
+ (pk11_ctx->session,
+ (CK_BYTE_PTR) data->base,
+ (CK_ULONG) data->length),
+ ISC_R_FAILURE);
+
+ return (ret);
+}
+
+static isc_result_t
+pkcs11rsa_sign(dst_context_t *dctx, isc_buffer_t *sig) {
+ CK_RV rv;
+ CK_MECHANISM mech = { CKM_RSA_PKCS, NULL, 0 };
+ CK_OBJECT_HANDLE hKey = CK_INVALID_HANDLE;
+ CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY;
+ CK_KEY_TYPE keyType = CKK_RSA;
+ CK_ATTRIBUTE keyTemplate[] =
+ {
+ { CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+ { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+ { CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_SENSITIVE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_SIGN, &truevalue, (CK_ULONG) sizeof(truevalue) },
+ { CKA_MODULUS, NULL, 0 },
+ { CKA_PUBLIC_EXPONENT, NULL, 0 },
+ { CKA_PRIVATE_EXPONENT, NULL, 0 },
+ { CKA_PRIME_1, NULL, 0 },
+ { CKA_PRIME_2, NULL, 0 },
+ { CKA_EXPONENT_1, NULL, 0 },
+ { CKA_EXPONENT_2, NULL, 0 },
+ { CKA_COEFFICIENT, NULL, 0 }
+ };
+ CK_ATTRIBUTE *attr;
+ CK_BYTE digest[MAX_DER_SIZE + ISC_SHA512_DIGESTLENGTH];
+ CK_BYTE *der;
+ CK_ULONG derlen;
+ CK_ULONG hashlen;
+ CK_ULONG dgstlen;
+ CK_ULONG siglen = 0;
+ pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx;
+ dst_key_t *key = dctx->key;
+ pk11_object_t *rsa = key->keydata.pkey;
+ isc_region_t r;
+ isc_result_t ret = ISC_R_SUCCESS;
+ unsigned int i;
+
+#ifndef PK11_MD5_DISABLE
+ REQUIRE(key->key_alg == DST_ALG_RSAMD5 ||
+ key->key_alg == DST_ALG_RSASHA1 ||
+ key->key_alg == DST_ALG_NSEC3RSASHA1 ||
+ key->key_alg == DST_ALG_RSASHA256 ||
+ key->key_alg == DST_ALG_RSASHA512);
+#else
+ REQUIRE(key->key_alg == DST_ALG_RSASHA1 ||
+ key->key_alg == DST_ALG_NSEC3RSASHA1 ||
+ key->key_alg == DST_ALG_RSASHA256 ||
+ key->key_alg == DST_ALG_RSASHA512);
+#endif
+ REQUIRE(rsa != NULL);
+
+ /*
+ * Reject incorrect RSA key lengths.
+ */
+ switch (dctx->key->key_alg) {
+ case DST_ALG_RSAMD5:
+ case DST_ALG_RSASHA1:
+ case DST_ALG_NSEC3RSASHA1:
+ /* From RFC 3110 */
+ if (dctx->key->key_size > 4096)
+ return (ISC_R_FAILURE);
+ break;
+ case DST_ALG_RSASHA256:
+ /* From RFC 5702 */
+ if ((dctx->key->key_size < 512) ||
+ (dctx->key->key_size > 4096))
+ return (ISC_R_FAILURE);
+ break;
+ case DST_ALG_RSASHA512:
+ /* From RFC 5702 */
+ if ((dctx->key->key_size < 1024) ||
+ (dctx->key->key_size > 4096))
+ return (ISC_R_FAILURE);
+ break;
+ default:
+ INSIST(0);
+ }
+
+ switch (key->key_alg) {
+#ifndef PK11_MD5_DISABLE
+ case DST_ALG_RSAMD5:
+ der = md5_der;
+ derlen = sizeof(md5_der);
+ hashlen = ISC_MD5_DIGESTLENGTH;
+ break;
+#endif
+ case DST_ALG_RSASHA1:
+ case DST_ALG_NSEC3RSASHA1:
+ der = sha1_der;
+ derlen = sizeof(sha1_der);
+ hashlen = ISC_SHA1_DIGESTLENGTH;
+ break;
+ case DST_ALG_RSASHA256:
+ der = sha256_der;
+ derlen = sizeof(sha256_der);
+ hashlen = ISC_SHA256_DIGESTLENGTH;
+ break;
+ case DST_ALG_RSASHA512:
+ der = sha512_der;
+ derlen = sizeof(sha512_der);
+ hashlen = ISC_SHA512_DIGESTLENGTH;
+ break;
+ default:
+ INSIST(0);
+ }
+ dgstlen = derlen + hashlen;
+ INSIST(dgstlen <= sizeof(digest));
+ memmove(digest, der, derlen);
+
+ PK11_RET(pkcs_C_DigestFinal,
+ (pk11_ctx->session, digest + derlen, &hashlen),
+ DST_R_SIGNFAILURE);
+
+ isc_buffer_availableregion(sig, &r);
+ if (r.length < (unsigned int) dgstlen + MIN_PKCS1_PADLEN)
+ return (ISC_R_NOSPACE);
+
+ if (rsa->ontoken && (rsa->object != CK_INVALID_HANDLE)) {
+ pk11_ctx->ontoken = rsa->ontoken;
+ pk11_ctx->object = rsa->object;
+ goto token_key;
+ }
+
+ for (attr = pk11_attribute_first(rsa);
+ attr != NULL;
+ attr = pk11_attribute_next(rsa, attr))
+ switch (attr->type) {
+ case CKA_MODULUS:
+ INSIST(keyTemplate[6].type == attr->type);
+ keyTemplate[6].pValue = isc_mem_get(dctx->mctx,
+ attr->ulValueLen);
+ if (keyTemplate[6].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[6].pValue, attr->pValue,
+ attr->ulValueLen);
+ keyTemplate[6].ulValueLen = attr->ulValueLen;
+ break;
+ case CKA_PUBLIC_EXPONENT:
+ INSIST(keyTemplate[7].type == attr->type);
+ keyTemplate[7].pValue = isc_mem_get(dctx->mctx,
+ attr->ulValueLen);
+ if (keyTemplate[7].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[7].pValue, attr->pValue,
+ attr->ulValueLen);
+ keyTemplate[7].ulValueLen = attr->ulValueLen;
+ break;
+ case CKA_PRIVATE_EXPONENT:
+ INSIST(keyTemplate[8].type == attr->type);
+ keyTemplate[8].pValue = isc_mem_get(dctx->mctx,
+ attr->ulValueLen);
+ if (keyTemplate[8].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[8].pValue, attr->pValue,
+ attr->ulValueLen);
+ keyTemplate[8].ulValueLen = attr->ulValueLen;
+ break;
+ case CKA_PRIME_1:
+ INSIST(keyTemplate[9].type == attr->type);
+ keyTemplate[9].pValue = isc_mem_get(dctx->mctx,
+ attr->ulValueLen);
+ if (keyTemplate[9].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[9].pValue, attr->pValue,
+ attr->ulValueLen);
+ keyTemplate[9].ulValueLen = attr->ulValueLen;
+ break;
+ case CKA_PRIME_2:
+ INSIST(keyTemplate[10].type == attr->type);
+ keyTemplate[10].pValue = isc_mem_get(dctx->mctx,
+ attr->ulValueLen);
+ if (keyTemplate[10].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[10].pValue, attr->pValue,
+ attr->ulValueLen);
+ keyTemplate[10].ulValueLen = attr->ulValueLen;
+ break;
+ case CKA_EXPONENT_1:
+ INSIST(keyTemplate[11].type == attr->type);
+ keyTemplate[11].pValue = isc_mem_get(dctx->mctx,
+ attr->ulValueLen);
+ if (keyTemplate[11].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[11].pValue, attr->pValue,
+ attr->ulValueLen);
+ keyTemplate[11].ulValueLen = attr->ulValueLen;
+ break;
+ case CKA_EXPONENT_2:
+ INSIST(keyTemplate[12].type == attr->type);
+ keyTemplate[12].pValue = isc_mem_get(dctx->mctx,
+ attr->ulValueLen);
+ if (keyTemplate[12].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[12].pValue, attr->pValue,
+ attr->ulValueLen);
+ keyTemplate[12].ulValueLen = attr->ulValueLen;
+ break;
+ case CKA_COEFFICIENT:
+ INSIST(keyTemplate[13].type == attr->type);
+ keyTemplate[13].pValue = isc_mem_get(dctx->mctx,
+ attr->ulValueLen);
+ if (keyTemplate[13].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[13].pValue, attr->pValue,
+ attr->ulValueLen);
+ keyTemplate[13].ulValueLen = attr->ulValueLen;
+ break;
+ }
+ pk11_ctx->object = CK_INVALID_HANDLE;
+ pk11_ctx->ontoken = false;
+ PK11_RET(pkcs_C_CreateObject,
+ (pk11_ctx->session,
+ keyTemplate, (CK_ULONG) 14,
+ &hKey),
+ ISC_R_FAILURE);
+
+ token_key:
+
+ PK11_RET(pkcs_C_SignInit,
+ (pk11_ctx->session, &mech,
+ pk11_ctx->ontoken ? pk11_ctx->object : hKey),
+ ISC_R_FAILURE);
+
+ PK11_RET(pkcs_C_Sign,
+ (pk11_ctx->session,
+ digest, dgstlen,
+ NULL, &siglen),
+ DST_R_SIGNFAILURE);
+
+ if (r.length < (unsigned int) siglen)
+ return (ISC_R_NOSPACE);
+
+ PK11_RET(pkcs_C_Sign,
+ (pk11_ctx->session,
+ digest, dgstlen,
+ (CK_BYTE_PTR) r.base, &siglen),
+ DST_R_SIGNFAILURE);
+
+ isc_buffer_add(sig, (unsigned int) siglen);
+
+ err:
+ if (hKey != CK_INVALID_HANDLE)
+ (void) pkcs_C_DestroyObject(pk11_ctx->session, hKey);
+ for (i = 6; i <= 13; i++)
+ if (keyTemplate[i].pValue != NULL) {
+ isc_safe_memwipe(keyTemplate[i].pValue,
+ keyTemplate[i].ulValueLen);
+ isc_mem_put(dctx->mctx,
+ keyTemplate[i].pValue,
+ keyTemplate[i].ulValueLen);
+ }
+ pk11_return_session(pk11_ctx);
+ isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx));
+ isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx));
+ dctx->ctxdata.pk11_ctx = NULL;
+
+ return (ret);
+}
+
+static isc_result_t
+pkcs11rsa_verify(dst_context_t *dctx, const isc_region_t *sig) {
+ CK_RV rv;
+ CK_MECHANISM mech = { CKM_RSA_PKCS, NULL, 0 };
+ CK_OBJECT_HANDLE hKey = CK_INVALID_HANDLE;
+ CK_OBJECT_CLASS keyClass = CKO_PUBLIC_KEY;
+ CK_KEY_TYPE keyType = CKK_RSA;
+ CK_ATTRIBUTE keyTemplate[] =
+ {
+ { CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+ { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+ { CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_VERIFY, &truevalue, (CK_ULONG) sizeof(truevalue) },
+ { CKA_MODULUS, NULL, 0 },
+ { CKA_PUBLIC_EXPONENT, NULL, 0 },
+ };
+ CK_ATTRIBUTE *attr;
+ CK_BYTE digest[MAX_DER_SIZE + ISC_SHA512_DIGESTLENGTH];
+ CK_BYTE *der;
+ CK_ULONG derlen;
+ CK_ULONG hashlen;
+ CK_ULONG dgstlen;
+ pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx;
+ dst_key_t *key = dctx->key;
+ pk11_object_t *rsa = key->keydata.pkey;
+ isc_result_t ret = ISC_R_SUCCESS;
+ unsigned int i;
+
+#ifndef PK11_MD5_DISABLE
+ REQUIRE(key->key_alg == DST_ALG_RSAMD5 ||
+ key->key_alg == DST_ALG_RSASHA1 ||
+ key->key_alg == DST_ALG_NSEC3RSASHA1 ||
+ key->key_alg == DST_ALG_RSASHA256 ||
+ key->key_alg == DST_ALG_RSASHA512);
+#else
+ REQUIRE(key->key_alg == DST_ALG_RSASHA1 ||
+ key->key_alg == DST_ALG_NSEC3RSASHA1 ||
+ key->key_alg == DST_ALG_RSASHA256 ||
+ key->key_alg == DST_ALG_RSASHA512);
+#endif
+ REQUIRE(rsa != NULL);
+
+ switch (key->key_alg) {
+#ifndef PK11_MD5_DISABLE
+ case DST_ALG_RSAMD5:
+ der = md5_der;
+ derlen = sizeof(md5_der);
+ hashlen = ISC_MD5_DIGESTLENGTH;
+ break;
+#endif
+ case DST_ALG_RSASHA1:
+ case DST_ALG_NSEC3RSASHA1:
+ der = sha1_der;
+ derlen = sizeof(sha1_der);
+ hashlen = ISC_SHA1_DIGESTLENGTH;
+ break;
+ case DST_ALG_RSASHA256:
+ der = sha256_der;
+ derlen = sizeof(sha256_der);
+ hashlen = ISC_SHA256_DIGESTLENGTH;
+ break;
+ case DST_ALG_RSASHA512:
+ der = sha512_der;
+ derlen = sizeof(sha512_der);
+ hashlen = ISC_SHA512_DIGESTLENGTH;
+ break;
+ default:
+ INSIST(0);
+ }
+ dgstlen = derlen + hashlen;
+ INSIST(dgstlen <= sizeof(digest));
+ memmove(digest, der, derlen);
+
+ PK11_RET(pkcs_C_DigestFinal,
+ (pk11_ctx->session, digest + derlen, &hashlen),
+ DST_R_SIGNFAILURE);
+
+ for (attr = pk11_attribute_first(rsa);
+ attr != NULL;
+ attr = pk11_attribute_next(rsa, attr))
+ switch (attr->type) {
+ case CKA_MODULUS:
+ INSIST(keyTemplate[5].type == attr->type);
+ keyTemplate[5].pValue = isc_mem_get(dctx->mctx,
+ attr->ulValueLen);
+ if (keyTemplate[5].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[5].pValue, attr->pValue,
+ attr->ulValueLen);
+ keyTemplate[5].ulValueLen = attr->ulValueLen;
+ break;
+ case CKA_PUBLIC_EXPONENT:
+ INSIST(keyTemplate[6].type == attr->type);
+ keyTemplate[6].pValue = isc_mem_get(dctx->mctx,
+ attr->ulValueLen);
+ if (keyTemplate[6].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(keyTemplate[6].pValue, attr->pValue,
+ attr->ulValueLen);
+ keyTemplate[6].ulValueLen = attr->ulValueLen;
+ if (pk11_numbits(attr->pValue,
+ attr->ulValueLen)
+ > RSA_MAX_PUBEXP_BITS)
+ DST_RET(DST_R_VERIFYFAILURE);
+ break;
+ }
+ pk11_ctx->object = CK_INVALID_HANDLE;
+ pk11_ctx->ontoken = false;
+ PK11_RET(pkcs_C_CreateObject,
+ (pk11_ctx->session,
+ keyTemplate, (CK_ULONG) 7,
+ &hKey),
+ ISC_R_FAILURE);
+
+ PK11_RET(pkcs_C_VerifyInit,
+ (pk11_ctx->session, &mech, hKey),
+ ISC_R_FAILURE);
+
+ PK11_RET(pkcs_C_Verify,
+ (pk11_ctx->session,
+ digest, dgstlen,
+ (CK_BYTE_PTR) sig->base, (CK_ULONG) sig->length),
+ DST_R_VERIFYFAILURE);
+
+ err:
+ if (hKey != CK_INVALID_HANDLE)
+ (void) pkcs_C_DestroyObject(pk11_ctx->session, hKey);
+ for (i = 5; i <= 6; i++)
+ if (keyTemplate[i].pValue != NULL) {
+ isc_safe_memwipe(keyTemplate[i].pValue,
+ keyTemplate[i].ulValueLen);
+ isc_mem_put(dctx->mctx,
+ keyTemplate[i].pValue,
+ keyTemplate[i].ulValueLen);
+ }
+ pk11_return_session(pk11_ctx);
+ isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx));
+ isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx));
+ dctx->ctxdata.pk11_ctx = NULL;
+
+ return (ret);
+}
+#endif
+
+static bool
+pkcs11rsa_compare(const dst_key_t *key1, const dst_key_t *key2) {
+ pk11_object_t *rsa1, *rsa2;
+ CK_ATTRIBUTE *attr1, *attr2;
+
+ rsa1 = key1->keydata.pkey;
+ rsa2 = key2->keydata.pkey;
+
+ if ((rsa1 == NULL) && (rsa2 == NULL))
+ return (true);
+ else if ((rsa1 == NULL) || (rsa2 == NULL))
+ return (false);
+
+ attr1 = pk11_attribute_bytype(rsa1, CKA_MODULUS);
+ attr2 = pk11_attribute_bytype(rsa2, CKA_MODULUS);
+ if ((attr1 == NULL) && (attr2 == NULL))
+ return (true);
+ else if ((attr1 == NULL) || (attr2 == NULL) ||
+ (attr1->ulValueLen != attr2->ulValueLen) ||
+ !isc_safe_memequal(attr1->pValue, attr2->pValue,
+ attr1->ulValueLen))
+ return (false);
+
+ attr1 = pk11_attribute_bytype(rsa1, CKA_PUBLIC_EXPONENT);
+ attr2 = pk11_attribute_bytype(rsa2, CKA_PUBLIC_EXPONENT);
+ if ((attr1 == NULL) && (attr2 == NULL))
+ return (true);
+ else if ((attr1 == NULL) || (attr2 == NULL) ||
+ (attr1->ulValueLen != attr2->ulValueLen) ||
+ !isc_safe_memequal(attr1->pValue, attr2->pValue,
+ attr1->ulValueLen))
+ return (false);
+
+ attr1 = pk11_attribute_bytype(rsa1, CKA_PRIVATE_EXPONENT);
+ attr2 = pk11_attribute_bytype(rsa2, CKA_PRIVATE_EXPONENT);
+ if (((attr1 != NULL) || (attr2 != NULL)) &&
+ ((attr1 == NULL) || (attr2 == NULL) ||
+ (attr1->ulValueLen != attr2->ulValueLen) ||
+ !isc_safe_memequal(attr1->pValue, attr2->pValue,
+ attr1->ulValueLen)))
+ return (false);
+
+ if (!rsa1->ontoken && !rsa2->ontoken)
+ return (true);
+ else if (rsa1->ontoken || rsa2->ontoken ||
+ (rsa1->object != rsa2->object))
+ return (false);
+
+ return (true);
+}
+
+static isc_result_t
+pkcs11rsa_generate(dst_key_t *key, int exp, void (*callback)(int)) {
+ CK_RV rv;
+ CK_MECHANISM mech = { CKM_RSA_PKCS_KEY_PAIR_GEN, NULL, 0 };
+ CK_OBJECT_HANDLE pub = CK_INVALID_HANDLE;
+ CK_ULONG bits = 0;
+ CK_BYTE pubexp[5];
+ CK_OBJECT_CLASS pubClass = CKO_PUBLIC_KEY;
+ CK_KEY_TYPE keyType = CKK_RSA;
+ CK_ATTRIBUTE pubTemplate[] =
+ {
+ { CKA_CLASS, &pubClass, (CK_ULONG) sizeof(pubClass) },
+ { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+ { CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_VERIFY, &truevalue, (CK_ULONG) sizeof(truevalue) },
+ { CKA_MODULUS_BITS, &bits, (CK_ULONG) sizeof(bits) },
+ { CKA_PUBLIC_EXPONENT, &pubexp, (CK_ULONG) sizeof(pubexp) }
+ };
+ CK_OBJECT_HANDLE priv = CK_INVALID_HANDLE;
+ CK_OBJECT_CLASS privClass = CKO_PRIVATE_KEY;
+ CK_ATTRIBUTE privTemplate[] =
+ {
+ { CKA_CLASS, &privClass, (CK_ULONG) sizeof(privClass) },
+ { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+ { CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_SENSITIVE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_EXTRACTABLE, &truevalue, (CK_ULONG) sizeof(truevalue) },
+ { CKA_SIGN, &truevalue, (CK_ULONG) sizeof(truevalue) },
+ };
+ CK_ATTRIBUTE *attr;
+ pk11_object_t *rsa;
+ pk11_context_t *pk11_ctx;
+ isc_result_t ret;
+ unsigned int i;
+
+ UNUSED(callback);
+
+ /*
+ * Reject incorrect RSA key lengths.
+ */
+ switch (key->key_alg) {
+ case DST_ALG_RSAMD5:
+ case DST_ALG_RSASHA1:
+ case DST_ALG_NSEC3RSASHA1:
+ /* From RFC 3110 */
+ if (key->key_size > 4096)
+ return (ISC_R_FAILURE);
+ break;
+ case DST_ALG_RSASHA256:
+ /* From RFC 5702 */
+ if ((key->key_size < 512) ||
+ (key->key_size > 4096))
+ return (ISC_R_FAILURE);
+ break;
+ case DST_ALG_RSASHA512:
+ /* From RFC 5702 */
+ if ((key->key_size < 1024) ||
+ (key->key_size > 4096))
+ return (ISC_R_FAILURE);
+ break;
+ default:
+ INSIST(0);
+ }
+
+ pk11_ctx = (pk11_context_t *) isc_mem_get(key->mctx,
+ sizeof(*pk11_ctx));
+ if (pk11_ctx == NULL)
+ return (ISC_R_NOMEMORY);
+ ret = pk11_get_session(pk11_ctx, OP_RSA, true, false,
+ false, NULL, pk11_get_best_token(OP_RSA));
+ if (ret != ISC_R_SUCCESS)
+ goto err;
+
+ bits = key->key_size;
+ if (exp == 0) {
+ /* RSA_F4 0x10001 */
+ pubexp[0] = 1;
+ pubexp[1] = 0;
+ pubexp[2] = 1;
+ pubTemplate[6].ulValueLen = 3;
+ } else {
+ /* F5 0x100000001 */
+ pubexp[0] = 1;
+ pubexp[1] = 0;
+ pubexp[2] = 0;
+ pubexp[3] = 0;
+ pubexp[4] = 1;
+ pubTemplate[6].ulValueLen = 5;
+ }
+
+ PK11_RET(pkcs_C_GenerateKeyPair,
+ (pk11_ctx->session, &mech,
+ pubTemplate, (CK_ULONG) 7,
+ privTemplate, (CK_ULONG) 7,
+ &pub, &priv),
+ DST_R_CRYPTOFAILURE);
+
+ rsa = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*rsa));
+ if (rsa == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(rsa, 0, sizeof(*rsa));
+ key->keydata.pkey = rsa;
+ rsa->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 8);
+ if (rsa->repr == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(rsa->repr, 0, sizeof(*attr) * 8);
+ rsa->attrcnt = 8;
+
+ attr = rsa->repr;
+ attr[0].type = CKA_MODULUS;
+ attr[1].type = CKA_PUBLIC_EXPONENT;
+ attr[2].type = CKA_PRIVATE_EXPONENT;
+ attr[3].type = CKA_PRIME_1;
+ attr[4].type = CKA_PRIME_2;
+ attr[5].type = CKA_EXPONENT_1;
+ attr[6].type = CKA_EXPONENT_2;
+ attr[7].type = CKA_COEFFICIENT;
+
+ PK11_RET(pkcs_C_GetAttributeValue,
+ (pk11_ctx->session, pub, attr, 2),
+ DST_R_CRYPTOFAILURE);
+ for (i = 0; i <= 1; i++) {
+ attr[i].pValue = isc_mem_get(key->mctx, attr[i].ulValueLen);
+ if (attr[i].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(attr[i].pValue, 0, attr[i].ulValueLen);
+ }
+ PK11_RET(pkcs_C_GetAttributeValue,
+ (pk11_ctx->session, pub, attr, 2),
+ DST_R_CRYPTOFAILURE);
+
+ attr += 2;
+ PK11_RET(pkcs_C_GetAttributeValue,
+ (pk11_ctx->session, priv, attr, 6),
+ DST_R_CRYPTOFAILURE);
+ for (i = 0; i <= 5; i++) {
+ attr[i].pValue = isc_mem_get(key->mctx, attr[i].ulValueLen);
+ if (attr[i].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(attr[i].pValue, 0, attr[i].ulValueLen);
+ }
+ PK11_RET(pkcs_C_GetAttributeValue,
+ (pk11_ctx->session, priv, attr, 6),
+ DST_R_CRYPTOFAILURE);
+
+ (void) pkcs_C_DestroyObject(pk11_ctx->session, priv);
+ (void) pkcs_C_DestroyObject(pk11_ctx->session, pub);
+ pk11_return_session(pk11_ctx);
+ isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx));
+ isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+ return (ISC_R_SUCCESS);
+
+ err:
+ pkcs11rsa_destroy(key);
+ if (priv != CK_INVALID_HANDLE)
+ (void) pkcs_C_DestroyObject(pk11_ctx->session, priv);
+ if (pub != CK_INVALID_HANDLE)
+ (void) pkcs_C_DestroyObject(pk11_ctx->session, pub);
+ pk11_return_session(pk11_ctx);
+ isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx));
+ isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+ return (ret);
+}
+
+static bool
+pkcs11rsa_isprivate(const dst_key_t *key) {
+ pk11_object_t *rsa = key->keydata.pkey;
+ CK_ATTRIBUTE *attr;
+
+ if (rsa == NULL)
+ return (false);
+ attr = pk11_attribute_bytype(rsa, CKA_PRIVATE_EXPONENT);
+ return (attr != NULL || rsa->ontoken);
+}
+
+static void
+pkcs11rsa_destroy(dst_key_t *key) {
+ pk11_object_t *rsa = key->keydata.pkey;
+ CK_ATTRIBUTE *attr;
+
+ if (rsa == NULL)
+ return;
+
+ INSIST((rsa->object == CK_INVALID_HANDLE) || rsa->ontoken);
+
+ for (attr = pk11_attribute_first(rsa);
+ attr != NULL;
+ attr = pk11_attribute_next(rsa, attr))
+ switch (attr->type) {
+ case CKA_LABEL:
+ case CKA_ID:
+ case CKA_MODULUS:
+ case CKA_PUBLIC_EXPONENT:
+ case CKA_PRIVATE_EXPONENT:
+ case CKA_PRIME_1:
+ case CKA_PRIME_2:
+ case CKA_EXPONENT_1:
+ case CKA_EXPONENT_2:
+ case CKA_COEFFICIENT:
+ if (attr->pValue != NULL) {
+ isc_safe_memwipe(attr->pValue,
+ attr->ulValueLen);
+ isc_mem_put(key->mctx,
+ attr->pValue,
+ attr->ulValueLen);
+ }
+ break;
+ }
+ if (rsa->repr != NULL) {
+ isc_safe_memwipe(rsa->repr, rsa->attrcnt * sizeof(*attr));
+ isc_mem_put(key->mctx,
+ rsa->repr,
+ rsa->attrcnt * sizeof(*attr));
+ }
+ isc_safe_memwipe(rsa, sizeof(*rsa));
+ isc_mem_put(key->mctx, rsa, sizeof(*rsa));
+ key->keydata.pkey = NULL;
+}
+
+static isc_result_t
+pkcs11rsa_todns(const dst_key_t *key, isc_buffer_t *data) {
+ pk11_object_t *rsa;
+ CK_ATTRIBUTE *attr;
+ isc_region_t r;
+ unsigned int e_bytes = 0, mod_bytes = 0;
+ CK_BYTE *exponent = NULL, *modulus = NULL;
+
+ REQUIRE(key->keydata.pkey != NULL);
+
+ rsa = key->keydata.pkey;
+
+ for (attr = pk11_attribute_first(rsa);
+ attr != NULL;
+ attr = pk11_attribute_next(rsa, attr))
+ switch (attr->type) {
+ case CKA_PUBLIC_EXPONENT:
+ exponent = (CK_BYTE *) attr->pValue;
+ e_bytes = (unsigned int) attr->ulValueLen;
+ break;
+ case CKA_MODULUS:
+ modulus = (CK_BYTE *) attr->pValue;
+ mod_bytes = (unsigned int) attr->ulValueLen;
+ break;
+ }
+ REQUIRE((exponent != NULL) && (modulus != NULL));
+
+ isc_buffer_availableregion(data, &r);
+
+ if (e_bytes < 256) { /*%< key exponent is <= 2040 bits */
+ if (r.length < 1)
+ return (ISC_R_NOSPACE);
+ isc_buffer_putuint8(data, (uint8_t) e_bytes);
+ isc_region_consume(&r, 1);
+ } else {
+ if (r.length < 3)
+ return (ISC_R_NOSPACE);
+ isc_buffer_putuint8(data, 0);
+ isc_buffer_putuint16(data, (uint16_t) e_bytes);
+ isc_region_consume(&r, 3);
+ }
+
+ if (r.length < e_bytes + mod_bytes)
+ return (ISC_R_NOSPACE);
+
+ memmove(r.base, exponent, e_bytes);
+ isc_region_consume(&r, e_bytes);
+ memmove(r.base, modulus, mod_bytes);
+
+ isc_buffer_add(data, e_bytes + mod_bytes);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+pkcs11rsa_fromdns(dst_key_t *key, isc_buffer_t *data) {
+ pk11_object_t *rsa;
+ isc_region_t r;
+ unsigned int e_bytes, mod_bytes;
+ CK_BYTE *exponent = NULL, *modulus = NULL;
+ CK_ATTRIBUTE *attr;
+ unsigned int length;
+
+ isc_buffer_remainingregion(data, &r);
+ if (r.length == 0)
+ return (ISC_R_SUCCESS);
+ length = r.length;
+
+ rsa = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*rsa));
+ if (rsa == NULL)
+ return (ISC_R_NOMEMORY);
+
+ memset(rsa, 0, sizeof(*rsa));
+
+ e_bytes = *r.base;
+ isc_region_consume(&r, 1);
+
+ if (e_bytes == 0) {
+ if (r.length < 2) {
+ isc_safe_memwipe(rsa, sizeof(*rsa));
+ isc_mem_put(key->mctx, rsa, sizeof(*rsa));
+ return (DST_R_INVALIDPUBLICKEY);
+ }
+ e_bytes = (*r.base) << 8;
+ isc_region_consume(&r, 1);
+ e_bytes += *r.base;
+ isc_region_consume(&r, 1);
+ }
+
+ if (r.length < e_bytes) {
+ isc_safe_memwipe(rsa, sizeof(*rsa));
+ isc_mem_put(key->mctx, rsa, sizeof(*rsa));
+ return (DST_R_INVALIDPUBLICKEY);
+ }
+ exponent = r.base;
+ isc_region_consume(&r, e_bytes);
+ modulus = r.base;
+ mod_bytes = r.length;
+
+ key->key_size = pk11_numbits(modulus, mod_bytes);
+
+ isc_buffer_forward(data, length);
+
+ rsa->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 2);
+ if (rsa->repr == NULL)
+ goto nomemory;
+ memset(rsa->repr, 0, sizeof(*attr) * 2);
+ rsa->attrcnt = 2;
+ attr = rsa->repr;
+ attr[0].type = CKA_MODULUS;
+ attr[0].pValue = isc_mem_get(key->mctx, mod_bytes);
+ if (attr[0].pValue == NULL)
+ goto nomemory;
+ memmove(attr[0].pValue, modulus, mod_bytes);
+ attr[0].ulValueLen = (CK_ULONG) mod_bytes;
+ attr[1].type = CKA_PUBLIC_EXPONENT;
+ attr[1].pValue = isc_mem_get(key->mctx, e_bytes);
+ if (attr[1].pValue == NULL)
+ goto nomemory;
+ memmove(attr[1].pValue, exponent, e_bytes);
+ attr[1].ulValueLen = (CK_ULONG) e_bytes;
+
+ key->keydata.pkey = rsa;
+
+ return (ISC_R_SUCCESS);
+
+ nomemory:
+ for (attr = pk11_attribute_first(rsa);
+ attr != NULL;
+ attr = pk11_attribute_next(rsa, attr))
+ switch (attr->type) {
+ case CKA_MODULUS:
+ case CKA_PUBLIC_EXPONENT:
+ if (attr->pValue != NULL) {
+ isc_safe_memwipe(attr->pValue,
+ attr->ulValueLen);
+ isc_mem_put(key->mctx,
+ attr->pValue,
+ attr->ulValueLen);
+ }
+ break;
+ }
+ if (rsa->repr != NULL) {
+ isc_safe_memwipe(rsa->repr,
+ rsa->attrcnt * sizeof(*attr));
+ isc_mem_put(key->mctx,
+ rsa->repr,
+ rsa->attrcnt * sizeof(*attr));
+ }
+ isc_safe_memwipe(rsa, sizeof(*rsa));
+ isc_mem_put(key->mctx, rsa, sizeof(*rsa));
+ return (ISC_R_NOMEMORY);
+}
+
+static isc_result_t
+pkcs11rsa_tofile(const dst_key_t *key, const char *directory) {
+ int i;
+ pk11_object_t *rsa;
+ CK_ATTRIBUTE *attr;
+ CK_ATTRIBUTE *modulus = NULL, *exponent = NULL;
+ CK_ATTRIBUTE *d = NULL, *p = NULL, *q = NULL;
+ CK_ATTRIBUTE *dmp1 = NULL, *dmq1 = NULL, *iqmp = NULL;
+ dst_private_t priv;
+ unsigned char *bufs[10];
+ isc_result_t result;
+
+ if (key->keydata.pkey == NULL)
+ return (DST_R_NULLKEY);
+
+ if (key->external) {
+ priv.nelements = 0;
+ return (dst__privstruct_writefile(key, &priv, directory));
+ }
+
+ rsa = key->keydata.pkey;
+
+ for (attr = pk11_attribute_first(rsa);
+ attr != NULL;
+ attr = pk11_attribute_next(rsa, attr))
+ switch (attr->type) {
+ case CKA_MODULUS:
+ modulus = attr;
+ break;
+ case CKA_PUBLIC_EXPONENT:
+ exponent = attr;
+ break;
+ case CKA_PRIVATE_EXPONENT:
+ d = attr;
+ break;
+ case CKA_PRIME_1:
+ p = attr;
+ break;
+ case CKA_PRIME_2:
+ q = attr;
+ break;
+ case CKA_EXPONENT_1:
+ dmp1 = attr;
+ break;
+ case CKA_EXPONENT_2:
+ dmq1 = attr;
+ break;
+ case CKA_COEFFICIENT:
+ iqmp = attr;
+ break;
+ }
+ if ((modulus == NULL) || (exponent == NULL))
+ return (DST_R_NULLKEY);
+
+ memset(bufs, 0, sizeof(bufs));
+
+ for (i = 0; i < 10; i++) {
+ bufs[i] = isc_mem_get(key->mctx, modulus->ulValueLen);
+ if (bufs[i] == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto fail;
+ }
+ memset(bufs[i], 0, modulus->ulValueLen);
+ }
+
+ i = 0;
+
+ priv.elements[i].tag = TAG_RSA_MODULUS;
+ priv.elements[i].length = (unsigned short) modulus->ulValueLen;
+ memmove(bufs[i], modulus->pValue, modulus->ulValueLen);
+ priv.elements[i].data = bufs[i];
+ i++;
+
+ priv.elements[i].tag = TAG_RSA_PUBLICEXPONENT;
+ priv.elements[i].length = (unsigned short) exponent->ulValueLen;
+ memmove(bufs[i], exponent->pValue, exponent->ulValueLen);
+ priv.elements[i].data = bufs[i];
+ i++;
+
+ if (d != NULL) {
+ priv.elements[i].tag = TAG_RSA_PRIVATEEXPONENT;
+ priv.elements[i].length = (unsigned short) d->ulValueLen;
+ memmove(bufs[i], d->pValue, d->ulValueLen);
+ priv.elements[i].data = bufs[i];
+ i++;
+ }
+
+ if (p != NULL) {
+ priv.elements[i].tag = TAG_RSA_PRIME1;
+ priv.elements[i].length = (unsigned short) p->ulValueLen;
+ memmove(bufs[i], p->pValue, p->ulValueLen);
+ priv.elements[i].data = bufs[i];
+ i++;
+ }
+
+ if (q != NULL) {
+ priv.elements[i].tag = TAG_RSA_PRIME2;
+ priv.elements[i].length = (unsigned short) q->ulValueLen;
+ memmove(bufs[i], q->pValue, q->ulValueLen);
+ priv.elements[i].data = bufs[i];
+ i++;
+ }
+
+ if (dmp1 != NULL) {
+ priv.elements[i].tag = TAG_RSA_EXPONENT1;
+ priv.elements[i].length = (unsigned short) dmp1->ulValueLen;
+ memmove(bufs[i], dmp1->pValue, dmp1->ulValueLen);
+ priv.elements[i].data = bufs[i];
+ i++;
+ }
+
+ if (dmq1 != NULL) {
+ priv.elements[i].tag = TAG_RSA_EXPONENT2;
+ priv.elements[i].length = (unsigned short) dmq1->ulValueLen;
+ memmove(bufs[i], dmq1->pValue, dmq1->ulValueLen);
+ priv.elements[i].data = bufs[i];
+ i++;
+ }
+
+ if (iqmp != NULL) {
+ priv.elements[i].tag = TAG_RSA_COEFFICIENT;
+ priv.elements[i].length = (unsigned short) iqmp->ulValueLen;
+ memmove(bufs[i], iqmp->pValue, iqmp->ulValueLen);
+ priv.elements[i].data = bufs[i];
+ i++;
+ }
+
+ if (key->engine != NULL) {
+ priv.elements[i].tag = TAG_RSA_ENGINE;
+ priv.elements[i].length =
+ (unsigned short)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 =
+ (unsigned short)strlen(key->label) + 1;
+ priv.elements[i].data = (unsigned char *)key->label;
+ i++;
+ }
+
+ priv.nelements = i;
+ result = dst__privstruct_writefile(key, &priv, directory);
+ fail:
+ for (i = 0; i < 10; i++) {
+ if (bufs[i] == NULL)
+ break;
+ isc_safe_memwipe(bufs[i], modulus->ulValueLen);
+ isc_mem_put(key->mctx, bufs[i], modulus->ulValueLen);
+ }
+ return (result);
+}
+
+static isc_result_t
+pkcs11rsa_fetch(dst_key_t *key, const char *engine, const char *label,
+ dst_key_t *pub)
+{
+ CK_RV rv;
+ CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY;
+ CK_KEY_TYPE keyType = CKK_RSA;
+ CK_ATTRIBUTE searchTemplate[] =
+ {
+ { CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+ { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+ { CKA_TOKEN, &truevalue, (CK_ULONG) sizeof(truevalue) },
+ { CKA_LABEL, NULL, 0 }
+ };
+ CK_ULONG cnt;
+ CK_ATTRIBUTE *attr;
+ CK_ATTRIBUTE *pubattr;
+ pk11_object_t *rsa;
+ pk11_object_t *pubrsa;
+ pk11_context_t *pk11_ctx = NULL;
+ isc_result_t ret;
+
+ if (label == NULL)
+ return (DST_R_NOENGINE);
+
+ rsa = key->keydata.pkey;
+ pubrsa = pub->keydata.pkey;
+
+ rsa->object = CK_INVALID_HANDLE;
+ rsa->ontoken = true;
+ rsa->reqlogon = true;
+ rsa->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 2);
+ if (rsa->repr == NULL)
+ return (ISC_R_NOMEMORY);
+ memset(rsa->repr, 0, sizeof(*attr) * 2);
+ rsa->attrcnt = 2;
+ attr = rsa->repr;
+
+ attr->type = CKA_MODULUS;
+ pubattr = pk11_attribute_bytype(pubrsa, CKA_MODULUS);
+ attr->pValue = isc_mem_get(key->mctx, pubattr->ulValueLen);
+ if (attr->pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(attr->pValue, pubattr->pValue, pubattr->ulValueLen);
+ attr->ulValueLen = pubattr->ulValueLen;
+ attr++;
+
+ attr->type = CKA_PUBLIC_EXPONENT;
+ pubattr = pk11_attribute_bytype(pubrsa, CKA_PUBLIC_EXPONENT);
+ attr->pValue = isc_mem_get(key->mctx, pubattr->ulValueLen);
+ if (attr->pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(attr->pValue, pubattr->pValue, pubattr->ulValueLen);
+ attr->ulValueLen = pubattr->ulValueLen;
+
+ ret = pk11_parse_uri(rsa, label, key->mctx, OP_RSA);
+ if (ret != ISC_R_SUCCESS)
+ goto err;
+
+ pk11_ctx = (pk11_context_t *) isc_mem_get(key->mctx,
+ sizeof(*pk11_ctx));
+ if (pk11_ctx == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ ret = pk11_get_session(pk11_ctx, OP_RSA, true, false,
+ rsa->reqlogon, NULL, rsa->slot);
+ if (ret != ISC_R_SUCCESS)
+ goto err;
+
+ attr = pk11_attribute_bytype(rsa, CKA_LABEL);
+ if (attr == NULL) {
+ attr = pk11_attribute_bytype(rsa, CKA_ID);
+ INSIST(attr != NULL);
+ searchTemplate[3].type = CKA_ID;
+ }
+ searchTemplate[3].pValue = attr->pValue;
+ searchTemplate[3].ulValueLen = attr->ulValueLen;
+
+ PK11_RET(pkcs_C_FindObjectsInit,
+ (pk11_ctx->session, searchTemplate, (CK_ULONG) 4),
+ DST_R_CRYPTOFAILURE);
+ PK11_RET(pkcs_C_FindObjects,
+ (pk11_ctx->session, &rsa->object, (CK_ULONG) 1, &cnt),
+ DST_R_CRYPTOFAILURE);
+ (void) pkcs_C_FindObjectsFinal(pk11_ctx->session);
+ if (cnt == 0)
+ DST_RET(ISC_R_NOTFOUND);
+ if (cnt > 1)
+ DST_RET(ISC_R_EXISTS);
+
+ if (engine != NULL) {
+ key->engine = isc_mem_strdup(key->mctx, engine);
+ 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);
+
+ pk11_return_session(pk11_ctx);
+ isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx));
+ isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+ attr = pk11_attribute_bytype(rsa, CKA_MODULUS);
+ INSIST(attr != NULL);
+ key->key_size = pk11_numbits(attr->pValue, attr->ulValueLen);
+
+ return (ISC_R_SUCCESS);
+
+ err:
+ if (pk11_ctx != NULL) {
+ pk11_return_session(pk11_ctx);
+ isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx));
+ isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+ }
+
+ return (ret);
+}
+
+static isc_result_t
+rsa_check(pk11_object_t *rsa, pk11_object_t *pubrsa) {
+ CK_ATTRIBUTE *pubattr, *privattr;
+ CK_BYTE *priv_exp = NULL, *priv_mod = NULL;
+ CK_BYTE *pub_exp = NULL, *pub_mod = NULL;
+ unsigned int priv_explen = 0, priv_modlen = 0;
+ unsigned int pub_explen = 0, pub_modlen = 0;
+
+ REQUIRE(rsa != NULL && pubrsa != NULL);
+
+ privattr = pk11_attribute_bytype(rsa, CKA_PUBLIC_EXPONENT);
+ INSIST(privattr != NULL);
+ priv_exp = privattr->pValue;
+ priv_explen = privattr->ulValueLen;
+
+ pubattr = pk11_attribute_bytype(pubrsa, CKA_PUBLIC_EXPONENT);
+ INSIST(pubattr != NULL);
+ pub_exp = pubattr->pValue;
+ pub_explen = pubattr->ulValueLen;
+
+ if (priv_exp != NULL) {
+ if (priv_explen != pub_explen)
+ return (DST_R_INVALIDPRIVATEKEY);
+ if (!isc_safe_memequal(priv_exp, pub_exp, pub_explen))
+ return (DST_R_INVALIDPRIVATEKEY);
+ } else {
+ privattr->pValue = pub_exp;
+ privattr->ulValueLen = pub_explen;
+ pubattr->pValue = NULL;
+ pubattr->ulValueLen = 0;
+ }
+
+ if (privattr->pValue == NULL)
+ return (DST_R_INVALIDPRIVATEKEY);
+
+ privattr = pk11_attribute_bytype(rsa, CKA_MODULUS);
+ INSIST(privattr != NULL);
+ priv_mod = privattr->pValue;
+ priv_modlen = privattr->ulValueLen;
+
+ pubattr = pk11_attribute_bytype(pubrsa, CKA_MODULUS);
+ INSIST(pubattr != NULL);
+ pub_mod = pubattr->pValue;
+ pub_modlen = pubattr->ulValueLen;
+
+ if (priv_mod != NULL) {
+ if (priv_modlen != pub_modlen)
+ return (DST_R_INVALIDPRIVATEKEY);
+ if (!isc_safe_memequal(priv_mod, pub_mod, pub_modlen))
+ return (DST_R_INVALIDPRIVATEKEY);
+ } else {
+ privattr->pValue = pub_mod;
+ privattr->ulValueLen = pub_modlen;
+ pubattr->pValue = NULL;
+ pubattr->ulValueLen = 0;
+ }
+
+ if (privattr->pValue == NULL)
+ return (DST_R_INVALIDPRIVATEKEY);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+pkcs11rsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
+ dst_private_t priv;
+ isc_result_t ret;
+ int i;
+ pk11_object_t *rsa;
+ CK_ATTRIBUTE *attr;
+ isc_mem_t *mctx = key->mctx;
+ const char *engine = NULL, *label = NULL;
+
+ /* read private key file */
+ ret = dst__privstruct_parse(key, DST_ALG_RSA, lexer, mctx, &priv);
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+
+ if (key->external) {
+ if (priv.nelements != 0)
+ DST_RET(DST_R_INVALIDPRIVATEKEY);
+ if (pub == NULL)
+ DST_RET(DST_R_INVALIDPRIVATEKEY);
+
+ key->keydata.pkey = pub->keydata.pkey;
+ pub->keydata.pkey = NULL;
+ key->key_size = pub->key_size;
+
+ dst__privstruct_free(&priv, mctx);
+ isc_safe_memwipe(&priv, sizeof(priv));
+
+ return (ISC_R_SUCCESS);
+ }
+
+ for (i = 0; i < priv.nelements; i++) {
+ switch (priv.elements[i].tag) {
+ case TAG_RSA_ENGINE:
+ engine = (char *)priv.elements[i].data;
+ break;
+ case TAG_RSA_LABEL:
+ label = (char *)priv.elements[i].data;
+ break;
+ default:
+ break;
+ }
+ }
+ rsa = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*rsa));
+ if (rsa == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(rsa, 0, sizeof(*rsa));
+ key->keydata.pkey = rsa;
+
+ /* Is this key is stored in a HSM? See if we can fetch it. */
+ if ((label != NULL) || (engine != NULL)) {
+ ret = pkcs11rsa_fetch(key, engine, label, pub);
+ if (ret != ISC_R_SUCCESS)
+ goto err;
+ dst__privstruct_free(&priv, mctx);
+ isc_safe_memwipe(&priv, sizeof(priv));
+ return (ret);
+ }
+
+ rsa->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 8);
+ if (rsa->repr == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(rsa->repr, 0, sizeof(*attr) * 8);
+ rsa->attrcnt = 8;
+ attr = rsa->repr;
+ attr[0].type = CKA_MODULUS;
+ attr[1].type = CKA_PUBLIC_EXPONENT;
+ attr[2].type = CKA_PRIVATE_EXPONENT;
+ attr[3].type = CKA_PRIME_1;
+ attr[4].type = CKA_PRIME_2;
+ attr[5].type = CKA_EXPONENT_1;
+ attr[6].type = CKA_EXPONENT_2;
+ attr[7].type = CKA_COEFFICIENT;
+
+ for (i = 0; i < priv.nelements; i++) {
+ CK_BYTE *bn;
+
+ switch (priv.elements[i].tag) {
+ case TAG_RSA_ENGINE:
+ continue;
+ case TAG_RSA_LABEL:
+ continue;
+ default:
+ bn = isc_mem_get(key->mctx, priv.elements[i].length);
+ if (bn == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memmove(bn, priv.elements[i].data,
+ priv.elements[i].length);
+ }
+
+ switch (priv.elements[i].tag) {
+ case TAG_RSA_MODULUS:
+ attr = pk11_attribute_bytype(rsa, CKA_MODULUS);
+ INSIST(attr != NULL);
+ attr->pValue = bn;
+ attr->ulValueLen = priv.elements[i].length;
+ break;
+ case TAG_RSA_PUBLICEXPONENT:
+ attr = pk11_attribute_bytype(rsa,
+ CKA_PUBLIC_EXPONENT);
+ INSIST(attr != NULL);
+ attr->pValue = bn;
+ attr->ulValueLen = priv.elements[i].length;
+ break;
+ case TAG_RSA_PRIVATEEXPONENT:
+ attr = pk11_attribute_bytype(rsa,
+ CKA_PRIVATE_EXPONENT);
+ INSIST(attr != NULL);
+ attr->pValue = bn;
+ attr->ulValueLen = priv.elements[i].length;
+ break;
+ case TAG_RSA_PRIME1:
+ attr = pk11_attribute_bytype(rsa, CKA_PRIME_1);
+ INSIST(attr != NULL);
+ attr->pValue = bn;
+ attr->ulValueLen = priv.elements[i].length;
+ break;
+ case TAG_RSA_PRIME2:
+ attr = pk11_attribute_bytype(rsa, CKA_PRIME_2);
+ INSIST(attr != NULL);
+ attr->pValue = bn;
+ attr->ulValueLen = priv.elements[i].length;
+ break;
+ case TAG_RSA_EXPONENT1:
+ attr = pk11_attribute_bytype(rsa,
+ CKA_EXPONENT_1);
+ INSIST(attr != NULL);
+ attr->pValue = bn;
+ attr->ulValueLen = priv.elements[i].length;
+ break;
+ case TAG_RSA_EXPONENT2:
+ attr = pk11_attribute_bytype(rsa,
+ CKA_EXPONENT_2);
+ INSIST(attr != NULL);
+ attr->pValue = bn;
+ attr->ulValueLen = priv.elements[i].length;
+ break;
+ case TAG_RSA_COEFFICIENT:
+ attr = pk11_attribute_bytype(rsa,
+ CKA_COEFFICIENT);
+ INSIST(attr != NULL);
+ attr->pValue = bn;
+ attr->ulValueLen = priv.elements[i].length;
+ break;
+ }
+ }
+
+ if (rsa_check(rsa, pub->keydata.pkey) != ISC_R_SUCCESS)
+ DST_RET(DST_R_INVALIDPRIVATEKEY);
+
+ attr = pk11_attribute_bytype(rsa, CKA_MODULUS);
+ INSIST(attr != NULL);
+ key->key_size = pk11_numbits(attr->pValue, attr->ulValueLen);
+
+ attr = pk11_attribute_bytype(rsa, CKA_PUBLIC_EXPONENT);
+ INSIST(attr != NULL);
+ if (pk11_numbits(attr->pValue, attr->ulValueLen) > RSA_MAX_PUBEXP_BITS)
+ DST_RET(ISC_R_RANGE);
+
+ dst__privstruct_free(&priv, mctx);
+ isc_safe_memwipe(&priv, sizeof(priv));
+
+ return (ISC_R_SUCCESS);
+
+ err:
+ pkcs11rsa_destroy(key);
+ dst__privstruct_free(&priv, mctx);
+ isc_safe_memwipe(&priv, sizeof(priv));
+ return (ret);
+}
+
+static isc_result_t
+pkcs11rsa_fromlabel(dst_key_t *key, const char *engine, const char *label,
+ const char *pin)
+{
+ CK_RV rv;
+ CK_OBJECT_HANDLE hKey = CK_INVALID_HANDLE;
+ CK_OBJECT_CLASS keyClass = CKO_PUBLIC_KEY;
+ CK_KEY_TYPE keyType = CKK_RSA;
+ CK_ATTRIBUTE searchTemplate[] =
+ {
+ { CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+ { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+ { CKA_TOKEN, &truevalue, (CK_ULONG) sizeof(truevalue) },
+ { CKA_LABEL, NULL, 0 }
+ };
+ CK_ULONG cnt;
+ CK_ATTRIBUTE *attr;
+ pk11_object_t *rsa;
+ pk11_context_t *pk11_ctx = NULL;
+ isc_result_t ret;
+ unsigned int i;
+
+ UNUSED(pin);
+
+ rsa = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*rsa));
+ if (rsa == NULL)
+ return (ISC_R_NOMEMORY);
+ memset(rsa, 0, sizeof(*rsa));
+ rsa->object = CK_INVALID_HANDLE;
+ rsa->ontoken = true;
+ rsa->reqlogon = true;
+ key->keydata.pkey = rsa;
+
+ rsa->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 2);
+ if (rsa->repr == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(rsa->repr, 0, sizeof(*attr) * 2);
+ rsa->attrcnt = 2;
+ attr = rsa->repr;
+ attr[0].type = CKA_MODULUS;
+ attr[1].type = CKA_PUBLIC_EXPONENT;
+
+ ret = pk11_parse_uri(rsa, label, key->mctx, OP_RSA);
+ if (ret != ISC_R_SUCCESS)
+ goto err;
+
+ pk11_ctx = (pk11_context_t *) isc_mem_get(key->mctx,
+ sizeof(*pk11_ctx));
+ if (pk11_ctx == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ ret = pk11_get_session(pk11_ctx, OP_RSA, true, false,
+ rsa->reqlogon, NULL, rsa->slot);
+ if (ret != ISC_R_SUCCESS)
+ goto err;
+
+ attr = pk11_attribute_bytype(rsa, CKA_LABEL);
+ if (attr == NULL) {
+ attr = pk11_attribute_bytype(rsa, CKA_ID);
+ INSIST(attr != NULL);
+ searchTemplate[3].type = CKA_ID;
+ }
+ searchTemplate[3].pValue = attr->pValue;
+ searchTemplate[3].ulValueLen = attr->ulValueLen;
+
+ PK11_RET(pkcs_C_FindObjectsInit,
+ (pk11_ctx->session, searchTemplate, (CK_ULONG) 4),
+ DST_R_CRYPTOFAILURE);
+ PK11_RET(pkcs_C_FindObjects,
+ (pk11_ctx->session, &hKey, (CK_ULONG) 1, &cnt),
+ DST_R_CRYPTOFAILURE);
+ (void) pkcs_C_FindObjectsFinal(pk11_ctx->session);
+ if (cnt == 0)
+ DST_RET(ISC_R_NOTFOUND);
+ if (cnt > 1)
+ DST_RET(ISC_R_EXISTS);
+
+ attr = rsa->repr;
+ PK11_RET(pkcs_C_GetAttributeValue,
+ (pk11_ctx->session, hKey, attr, 2),
+ DST_R_CRYPTOFAILURE);
+ for (i = 0; i <= 1; i++) {
+ attr[i].pValue = isc_mem_get(key->mctx, attr[i].ulValueLen);
+ if (attr[i].pValue == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ memset(attr[i].pValue, 0, attr[i].ulValueLen);
+ }
+ PK11_RET(pkcs_C_GetAttributeValue,
+ (pk11_ctx->session, hKey, attr, 2),
+ DST_R_CRYPTOFAILURE);
+
+ keyClass = CKO_PRIVATE_KEY;
+ PK11_RET(pkcs_C_FindObjectsInit,
+ (pk11_ctx->session, searchTemplate, (CK_ULONG) 4),
+ DST_R_CRYPTOFAILURE);
+ PK11_RET(pkcs_C_FindObjects,
+ (pk11_ctx->session, &rsa->object, (CK_ULONG) 1, &cnt),
+ DST_R_CRYPTOFAILURE);
+ (void) pkcs_C_FindObjectsFinal(pk11_ctx->session);
+ if (cnt == 0)
+ DST_RET(ISC_R_NOTFOUND);
+ if (cnt > 1)
+ DST_RET(ISC_R_EXISTS);
+
+ if (engine != NULL) {
+ key->engine = isc_mem_strdup(key->mctx, engine);
+ 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);
+
+ attr = pk11_attribute_bytype(rsa, CKA_PUBLIC_EXPONENT);
+ INSIST(attr != NULL);
+ if (pk11_numbits(attr->pValue, attr->ulValueLen) > RSA_MAX_PUBEXP_BITS)
+ DST_RET(ISC_R_RANGE);
+
+ attr = pk11_attribute_bytype(rsa, CKA_MODULUS);
+ INSIST(attr != NULL);
+ key->key_size = pk11_numbits(attr->pValue, attr->ulValueLen);
+
+ pk11_return_session(pk11_ctx);
+ isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx));
+ isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+ return (ISC_R_SUCCESS);
+
+ err:
+ pkcs11rsa_destroy(key);
+ if (pk11_ctx != NULL) {
+ pk11_return_session(pk11_ctx);
+ isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx));
+ isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+ }
+
+ return (ret);
+}
+
+static dst_func_t pkcs11rsa_functions = {
+ pkcs11rsa_createctx,
+#ifndef PK11_RSA_PKCS_REPLACE
+ pkcs11rsa_createctx2,
+#else
+ NULL, /*%< createctx2 */
+#endif
+ pkcs11rsa_destroyctx,
+ pkcs11rsa_adddata,
+ pkcs11rsa_sign,
+ pkcs11rsa_verify,
+ NULL, /*%< verify2 */
+ NULL, /*%< computesecret */
+ pkcs11rsa_compare,
+ NULL, /*%< paramcompare */
+ pkcs11rsa_generate,
+ pkcs11rsa_isprivate,
+ pkcs11rsa_destroy,
+ pkcs11rsa_todns,
+ pkcs11rsa_fromdns,
+ pkcs11rsa_tofile,
+ pkcs11rsa_parse,
+ NULL, /*%< cleanup */
+ pkcs11rsa_fromlabel,
+ NULL, /*%< dump */
+ NULL, /*%< restore */
+};
+
+isc_result_t
+dst__pkcs11rsa_init(dst_func_t **funcp) {
+ REQUIRE(funcp != NULL);
+
+ if (*funcp == NULL)
+ *funcp = &pkcs11rsa_functions;
+ return (ISC_R_SUCCESS);
+}
+
+#else /* PKCS11CRYPTO */
+
+#include <isc/util.h>
+
+EMPTY_TRANSLATION_UNIT
+
+#endif /* PKCS11CRYPTO */
+/*! \file */
diff --git a/lib/dns/portlist.c b/lib/dns/portlist.c
new file mode 100644
index 0000000..7de7178
--- /dev/null
+++ b/lib/dns/portlist.c
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.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;
+ 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) {
+ memmove(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);
+}
+
+bool
+dns_portlist_match(dns_portlist_t *portlist, int af, in_port_t port) {
+ dns_element_t *el;
+ bool result = 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 = true;
+ if (af == AF_INET6 && (el->flags & DNS_PL_INET6) != 0)
+ result = 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/private.c b/lib/dns/private.c
new file mode 100644
index 0000000..b694f8c
--- /dev/null
+++ b/lib/dns/private.c
@@ -0,0 +1,367 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include "config.h"
+
+#include <stdbool.h>
+
+#include <isc/base64.h>
+#include <isc/print.h>
+#include <isc/result.h>
+#include <isc/string.h>
+#include <isc/types.h>
+#include <isc/util.h>
+
+#include <dns/nsec3.h>
+#include <dns/private.h>
+
+/*
+ * We need to build the relevant chain if there exists a NSEC/NSEC3PARAM
+ * at the apex; normally only one or the other of NSEC/NSEC3PARAM will exist.
+ *
+ * If a NSEC3PARAM RRset exists then we will need to build a NSEC chain
+ * if all the NSEC3PARAM records (and associated chains) are slated for
+ * destruction and we have not been told to NOT build the NSEC chain.
+ *
+ * If the NSEC set exist then check to see if there is a request to create
+ * a NSEC3 chain.
+ *
+ * If neither NSEC/NSEC3PARAM RRsets exist at the origin and the private
+ * type exists then we need to examine it to determine if NSEC3 chain has
+ * been requested to be built otherwise a NSEC chain needs to be built.
+ */
+
+#define REMOVE(x) (((x) & DNS_NSEC3FLAG_REMOVE) != 0)
+#define CREATE(x) (((x) & DNS_NSEC3FLAG_CREATE) != 0)
+#define INITIAL(x) (((x) & DNS_NSEC3FLAG_INITIAL) != 0)
+#define NONSEC(x) (((x) & DNS_NSEC3FLAG_NONSEC) != 0)
+
+#define CHECK(x) do { \
+ result = (x); \
+ if (result != ISC_R_SUCCESS) \
+ goto failure; \
+ } while (0)
+
+/*
+ * Work out if 'param' should be ignored or not (i.e. it is in the process
+ * of being removed).
+ *
+ * Note: we 'belt-and-braces' here by also checking for a CREATE private
+ * record and keep the param record in this case.
+ */
+
+static bool
+ignore(dns_rdata_t *param, dns_rdataset_t *privateset) {
+ isc_result_t result;
+
+ for (result = dns_rdataset_first(privateset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(privateset)) {
+ unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE];
+ dns_rdata_t private = DNS_RDATA_INIT;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+
+ dns_rdataset_current(privateset, &private);
+ if (!dns_nsec3param_fromprivate(&private, &rdata,
+ buf, sizeof(buf)))
+ continue;
+ /*
+ * We are going to create a new NSEC3 chain so it
+ * doesn't matter if we are removing this one.
+ */
+ if (CREATE(rdata.data[1]))
+ return (false);
+ if (rdata.data[0] != param->data[0] ||
+ 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;
+ /*
+ * The removal of this NSEC3 chain does NOT cause a
+ * NSEC chain to be created so we don't need to tell
+ * the caller that it will be removed.
+ */
+ if (NONSEC(rdata.data[1]))
+ return (false);
+ return (true);
+ }
+ return (false);
+}
+
+isc_result_t
+dns_private_chains(dns_db_t *db, dns_dbversion_t *ver,
+ dns_rdatatype_t privatetype,
+ bool *build_nsec, bool *build_nsec3)
+{
+ dns_dbnode_t *node;
+ dns_rdataset_t nsecset, nsec3paramset, privateset;
+ bool nsec3chain;
+ bool signing;
+ isc_result_t result;
+ unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE];
+ unsigned int count;
+
+ node = NULL;
+ dns_rdataset_init(&nsecset);
+ dns_rdataset_init(&nsec3paramset);
+ dns_rdataset_init(&privateset);
+
+ CHECK(dns_db_getoriginnode(db, &node));
+
+ result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec,
+ 0, (isc_stdtime_t) 0, &nsecset, NULL);
+
+ if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND)
+ goto failure;
+
+ result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3param,
+ 0, (isc_stdtime_t) 0, &nsec3paramset,
+ NULL);
+ if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND)
+ goto failure;
+
+ if (dns_rdataset_isassociated(&nsecset) &&
+ dns_rdataset_isassociated(&nsec3paramset)) {
+ if (build_nsec != NULL)
+ *build_nsec = true;
+ if (build_nsec3 != NULL)
+ *build_nsec3 = true;
+ goto success;
+ }
+
+ if (privatetype != (dns_rdatatype_t)0) {
+ result = dns_db_findrdataset(db, node, ver, privatetype,
+ 0, (isc_stdtime_t) 0,
+ &privateset, NULL);
+ if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND)
+ goto failure;
+ }
+
+ /*
+ * Look to see if we also need to be creating a NSEC3 chain.
+ */
+ if (dns_rdataset_isassociated(&nsecset)) {
+ if (build_nsec != NULL)
+ *build_nsec = true;
+ if (build_nsec3 != NULL)
+ *build_nsec3 = false;
+ if (!dns_rdataset_isassociated(&privateset))
+ goto success;
+ for (result = dns_rdataset_first(&privateset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&privateset)) {
+ dns_rdata_t private = DNS_RDATA_INIT;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+
+ dns_rdataset_current(&privateset, &private);
+ if (!dns_nsec3param_fromprivate(&private, &rdata,
+ buf, sizeof(buf)))
+ continue;
+ if (REMOVE(rdata.data[1]))
+ continue;
+ if (build_nsec3 != NULL)
+ *build_nsec3 = true;
+ break;
+ }
+ goto success;
+ }
+
+ if (dns_rdataset_isassociated(&nsec3paramset)) {
+ if (build_nsec3 != NULL)
+ *build_nsec3 = true;
+ if (build_nsec != NULL)
+ *build_nsec = false;
+ if (!dns_rdataset_isassociated(&privateset))
+ goto success;
+ /*
+ * If we are in the process of building a new NSEC3 chain
+ * then we don't need to build a NSEC chain.
+ */
+ for (result = dns_rdataset_first(&privateset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&privateset)) {
+ dns_rdata_t private = DNS_RDATA_INIT;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+
+ dns_rdataset_current(&privateset, &private);
+ if (!dns_nsec3param_fromprivate(&private, &rdata,
+ buf, sizeof(buf)))
+ continue;
+ if (CREATE(rdata.data[1]))
+ goto success;
+ }
+
+ /*
+ * Check to see if there will be a active NSEC3CHAIN once
+ * the changes queued complete.
+ */
+ count = 0;
+ for (result = dns_rdataset_first(&nsec3paramset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&nsec3paramset)) {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+
+ /*
+ * If there is more that one NSEC3 chain present then
+ * we don't need to construct a NSEC chain.
+ */
+ if (++count > 1)
+ goto success;
+ dns_rdataset_current(&nsec3paramset, &rdata);
+ if (ignore(&rdata, &privateset))
+ continue;
+ /*
+ * We still have a good NSEC3 chain or we are
+ * not creating a NSEC chain as NONSEC is set.
+ */
+ goto success;
+ }
+
+ /*
+ * The last NSEC3 chain is being removed and does not have
+ * have NONSEC set.
+ */
+ if (build_nsec != NULL)
+ *build_nsec = true;
+ goto success;
+ }
+
+ if (build_nsec != NULL)
+ *build_nsec = false;
+ if (build_nsec3 != NULL)
+ *build_nsec3 = false;
+ if (!dns_rdataset_isassociated(&privateset))
+ goto success;
+
+ signing = false;
+ nsec3chain = false;
+
+ for (result = dns_rdataset_first(&privateset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&privateset)) {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_t private = DNS_RDATA_INIT;
+
+ dns_rdataset_current(&privateset, &private);
+ if (!dns_nsec3param_fromprivate(&private, &rdata,
+ buf, sizeof(buf))) {
+ /*
+ * Look for record that says we are signing the
+ * zone with a key.
+ */
+ if (private.length == 5 && private.data[0] != 0 &&
+ private.data[3] == 0 && private.data[4] == 0)
+ signing = true;
+ } else {
+ if (CREATE(rdata.data[1]))
+ nsec3chain = true;
+ }
+ }
+
+ if (signing) {
+ if (nsec3chain) {
+ if (build_nsec3 != NULL)
+ *build_nsec3 = true;
+ } else {
+ if (build_nsec != NULL)
+ *build_nsec = true;
+ }
+ }
+
+ success:
+ result = ISC_R_SUCCESS;
+ failure:
+ if (dns_rdataset_isassociated(&nsecset))
+ dns_rdataset_disassociate(&nsecset);
+ if (dns_rdataset_isassociated(&nsec3paramset))
+ dns_rdataset_disassociate(&nsec3paramset);
+ if (dns_rdataset_isassociated(&privateset))
+ dns_rdataset_disassociate(&privateset);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ return (result);
+}
+
+isc_result_t
+dns_private_totext(dns_rdata_t *private, isc_buffer_t *buf) {
+ isc_result_t result;
+
+ if (private->length < 5)
+ return (ISC_R_NOTFOUND);
+
+ if (private->data[0] == 0) {
+ unsigned char nsec3buf[DNS_NSEC3PARAM_BUFFERSIZE];
+ unsigned char newbuf[DNS_NSEC3PARAM_BUFFERSIZE];
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_nsec3param_t nsec3param;
+ bool del, init, nonsec;
+ isc_buffer_t b;
+
+ if (!dns_nsec3param_fromprivate(private, &rdata, nsec3buf,
+ sizeof(nsec3buf)))
+ CHECK(ISC_R_FAILURE);
+
+ CHECK(dns_rdata_tostruct(&rdata, &nsec3param, NULL));
+
+ del = (nsec3param.flags & DNS_NSEC3FLAG_REMOVE);
+ init = (nsec3param.flags & DNS_NSEC3FLAG_INITIAL);
+ nonsec = (nsec3param.flags & DNS_NSEC3FLAG_NONSEC);
+
+ nsec3param.flags &= ~(DNS_NSEC3FLAG_CREATE|
+ DNS_NSEC3FLAG_REMOVE|
+ DNS_NSEC3FLAG_INITIAL|
+ DNS_NSEC3FLAG_NONSEC);
+
+ if (init)
+ isc_buffer_putstr(buf, "Pending NSEC3 chain ");
+ else if (del)
+ isc_buffer_putstr(buf, "Removing NSEC3 chain ");
+ else
+ isc_buffer_putstr(buf, "Creating NSEC3 chain ");
+
+ dns_rdata_reset(&rdata);
+ isc_buffer_init(&b, newbuf, sizeof(newbuf));
+ CHECK(dns_rdata_fromstruct(&rdata, dns_rdataclass_in,
+ dns_rdatatype_nsec3param,
+ &nsec3param, &b));
+
+ CHECK(dns_rdata_totext(&rdata, NULL, buf));
+
+ if (del && !nonsec)
+ isc_buffer_putstr(buf, " / creating NSEC chain");
+ } else if (private->length == 5) {
+ unsigned char alg = private->data[0];
+ dns_keytag_t keyid = (private->data[2] | private->data[1] << 8);
+ char keybuf[BUFSIZ], algbuf[DNS_SECALG_FORMATSIZE];
+ bool del = (private->data[3] != 0);
+ bool complete = (private->data[4] != 0);
+
+ if (del && complete)
+ isc_buffer_putstr(buf, "Done removing signatures for ");
+ else if (del)
+ isc_buffer_putstr(buf, "Removing signatures for ");
+ else if (complete)
+ isc_buffer_putstr(buf, "Done signing with ");
+ else
+ isc_buffer_putstr(buf, "Signing with ");
+
+ dns_secalg_format(alg, algbuf, sizeof(algbuf));
+ snprintf(keybuf, sizeof(keybuf), "key %d/%s", keyid, algbuf);
+ isc_buffer_putstr(buf, keybuf);
+ } else
+ return (ISC_R_NOTFOUND);
+
+ isc_buffer_putuint8(buf, 0);
+ result = ISC_R_SUCCESS;
+ failure:
+ return (result);
+}
diff --git a/lib/dns/rbt.c b/lib/dns/rbt.c
new file mode 100644
index 0000000..62d0826
--- /dev/null
+++ b/lib/dns/rbt.c
@@ -0,0 +1,3672 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <sys/stat.h>
+#include <inttypes.h> /* uintptr_t */
+#include <stdbool.h>
+
+#include <isc/crc64.h>
+#include <isc/file.h>
+#include <isc/hex.h>
+#include <isc/mem.h>
+#include <isc/once.h>
+#include <isc/platform.h>
+#include <isc/print.h>
+#include <isc/refcount.h>
+#include <isc/socket.h>
+#include <isc/stdio.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>
+#include <dns/version.h>
+
+#include <unistd.h>
+
+#define CHECK(x) \
+ do { \
+ result = (x); \
+ if (result != ISC_R_SUCCESS) \
+ goto cleanup; \
+ } while (0)
+
+#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;
+ size_t hashsize;
+ dns_rbtnode_t ** hashtable;
+ void * mmap_location;
+};
+
+#define RED 0
+#define BLACK 1
+
+/*
+ * This is the header for map-format RBT images. It is populated,
+ * and then written, as the LAST thing done to the file before returning.
+ * Writing this last (with zeros in the header area initially) will ensure
+ * that the header is only valid when the RBT image is also valid.
+ */
+typedef struct file_header file_header_t;
+
+/* Pad to 32 bytes */
+static char FILE_VERSION[32] = "\0";
+
+/* Header length, always the same size regardless of structure size */
+#define HEADER_LENGTH 1024
+
+struct file_header {
+ char version1[32];
+ uint64_t first_node_offset; /* usually 1024 */
+ /*
+ * information about the system on which the map file was generated
+ * will be used to tell if we can load the map file or not
+ */
+ uint32_t ptrsize;
+ unsigned int bigendian:1; /* big or little endian system */
+ unsigned int rdataset_fixed:1; /* compiled with --enable-rrset-fixed */
+ unsigned int nodecount; /* shadow from rbt structure */
+ uint64_t crc;
+ char version2[32]; /* repeated; must match version1 */
+};
+
+/*
+ * The following declarations are for the serialization of an RBT:
+ *
+ * step one: write out a zeroed header of 1024 bytes
+ * step two: walk the tree in a depth-first, left-right-down order, writing
+ * out the nodes, reserving space as we go, correcting addresses to point
+ * at the proper offset in the file, and setting a flag for each pointer to
+ * indicate that it is a reference to a location in the file, rather than in
+ * memory.
+ * step three: write out the header, adding the information that will be
+ * needed to re-create the tree object itself.
+ *
+ * The RBTDB object will do this three times, once for each of the three
+ * RBT objects it contains.
+ *
+ * Note: 'file' must point an actual open file that can be mmapped
+ * and fseeked, not to a pipe or stream
+ */
+
+static isc_result_t
+dns_rbt_zero_header(FILE *file);
+
+static isc_result_t
+write_header(FILE *file, dns_rbt_t *rbt, uint64_t first_node_offset,
+ uint64_t crc);
+
+static bool
+match_header_version(file_header_t *header);
+
+static isc_result_t
+serialize_node(FILE *file, dns_rbtnode_t *node, uintptr_t left,
+ uintptr_t right, uintptr_t down, uintptr_t parent,
+ uintptr_t data, uint64_t *crc);
+
+static isc_result_t
+serialize_nodes(FILE *file, dns_rbtnode_t *node, uintptr_t parent,
+ dns_rbtdatawriter_t datawriter, void *writer_arg,
+ uintptr_t *where, uint64_t *crc);
+/*
+ * The following functions allow you to get the actual address of a pointer
+ * without having to use an if statement to check to see if that address is
+ * relative or not
+ */
+static inline dns_rbtnode_t *
+getparent(dns_rbtnode_t *node, file_header_t *header) {
+ char *adjusted_address = (char *)(node->parent);
+ adjusted_address += node->parent_is_relative * (uintptr_t)header;
+
+ return ((dns_rbtnode_t *)adjusted_address);
+}
+
+static inline dns_rbtnode_t *
+getleft(dns_rbtnode_t *node, file_header_t *header) {
+ char *adjusted_address = (char *)(node->left);
+ adjusted_address += node->left_is_relative * (uintptr_t)header;
+
+ return ((dns_rbtnode_t *)adjusted_address);
+}
+
+static inline dns_rbtnode_t *
+getright(dns_rbtnode_t *node, file_header_t *header) {
+ char *adjusted_address = (char *)(node->right);
+ adjusted_address += node->right_is_relative * (uintptr_t)header;
+
+ return ((dns_rbtnode_t *)adjusted_address);
+}
+
+static inline dns_rbtnode_t *
+getdown(dns_rbtnode_t *node, file_header_t *header) {
+ char *adjusted_address = (char *)(node->down);
+ adjusted_address += node->down_is_relative * (uintptr_t)header;
+
+ return ((dns_rbtnode_t *)adjusted_address);
+}
+
+static inline dns_rbtnode_t *
+getdata(dns_rbtnode_t *node, file_header_t *header) {
+ char *adjusted_address = (char *)(node->data);
+ adjusted_address += node->data_is_relative * (uintptr_t)header;
+
+ return ((dns_rbtnode_t *)adjusted_address);
+}
+
+/*%
+ * 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)
+#ifdef DNS_RBT_USEHASH
+#define UPPERNODE(node) ((node)->uppernode)
+#endif /* DNS_RBT_USEHASH */
+#define DATA(node) ((node)->data)
+#define IS_EMPTY(node) ((node)->data == NULL)
+#define HASHNEXT(node) ((node)->hashnext)
+#define HASHVAL(node) ((node)->hashval)
+#define COLOR(node) ((node)->color)
+#define NAMELEN(node) ((node)->namelen)
+#define OLDNAMELEN(node) ((node)->oldnamelen)
+#define OFFSETLEN(node) ((node)->offsetlen)
+#define ATTRS(node) ((node)->attributes)
+#define IS_ROOT(node) ((node)->is_root == 1)
+#define FINDCALLBACK(node) ((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 has the following
+ * structure.
+ *
+ * &lt;name_data&gt;{1..255}&lt;oldoffsetlen&gt;{1}&lt;offsets&gt;{1..128}
+ *
+ * &lt;name_data&gt; contains the name of the node when it was created.
+ * &lt;oldoffsetlen&gt; contains the length of &lt;offsets&gt; when the node was created.
+ * &lt;offsets&gt; contains the offets into name for each label when the node was
+ * created.
+ */
+
+#define NAME(node) ((unsigned char *)((node) + 1))
+#define OFFSETS(node) (NAME(node) + OLDNAMELEN(node) + 1)
+#define OLDOFFSETLEN(node) (OFFSETS(node)[-1])
+
+#define NODE_SIZE(node) (sizeof(*node) + \
+ OLDNAMELEN(node) + OLDOFFSETLEN(node) + 1)
+
+/*%
+ * 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 wholly handled by parent pointers (which didn't exist, because
+ * of memory concerns, when chains were first implemented).
+ */
+#define ADD_LEVEL(chain, node) \
+ do { \
+ INSIST((chain)->level_count < DNS_RBT_LEVELBLOCK); \
+ (chain)->levels[(chain)->level_count++] = (node); \
+ } while (0)
+
+/*%
+ * 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.
+ */
+
+static inline void
+NODENAME(dns_rbtnode_t *node, dns_name_t *name) {
+ 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;
+}
+
+void
+dns_rbtnode_nodename(dns_rbtnode_t *node, dns_name_t *name) {
+ 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;
+}
+
+dns_rbtnode_t *
+dns_rbt_root(dns_rbt_t *rbt) {
+ return rbt->root;
+}
+
+#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
+hexdump(const char *desc, unsigned char *data, size_t size) {
+ char hexdump[BUFSIZ * 2 + 1];
+ isc_buffer_t b;
+ isc_region_t r;
+ isc_result_t result;
+ size_t bytes;
+
+ fprintf(stderr, "%s: ", desc);
+ do {
+ isc_buffer_init(&b, hexdump, sizeof(hexdump));
+ r.base = data;
+ r.length = bytes = (size > BUFSIZ) ? BUFSIZ : size;
+ result = isc_hex_totext(&r, 0, "", &b);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ isc_buffer_putuint8(&b, 0);
+ fprintf(stderr, "%s", hexdump);
+ data += bytes;
+ size -= bytes;
+ } while (size > 0);
+ fprintf(stderr, "\n");
+}
+#endif /* DEBUG */
+
+#ifdef DNS_RBT_USEHASH
+
+/*
+ * Upper node is the parent of the root of the passed node's
+ * subtree. The passed node must not be NULL.
+ */
+static inline dns_rbtnode_t *
+get_upper_node(dns_rbtnode_t *node) {
+ return (UPPERNODE(node));
+}
+
+static void
+fixup_uppernodes_helper(dns_rbtnode_t *node, dns_rbtnode_t *uppernode) {
+ if (node == NULL)
+ return;
+
+ UPPERNODE(node) = uppernode;
+
+ fixup_uppernodes_helper(LEFT(node), uppernode);
+ fixup_uppernodes_helper(RIGHT(node), uppernode);
+ fixup_uppernodes_helper(DOWN(node), node);
+}
+
+/*
+ * This function is used to fixup uppernode members of all dns_rbtnodes
+ * after deserialization.
+ */
+static void
+fixup_uppernodes(dns_rbt_t *rbt) {
+ fixup_uppernodes_helper(rbt->root, NULL);
+}
+
+#else
+
+/* The passed node must not be NULL. */
+static inline dns_rbtnode_t *
+get_subtree_root(dns_rbtnode_t *node) {
+ while (!IS_ROOT(node)) {
+ node = PARENT(node);
+ }
+
+ return (node);
+}
+
+/* Upper node is the parent of the root of the passed node's
+ * subtree. The passed node must not be NULL.
+ */
+static inline dns_rbtnode_t *
+get_upper_node(dns_rbtnode_t *node) {
+ dns_rbtnode_t *root = get_subtree_root(node);
+
+ /*
+ * 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.
+ */
+ return (PARENT(root));
+}
+
+#endif /* DNS_RBT_USEHASH */
+
+size_t
+dns__rbtnode_getdistance(dns_rbtnode_t *node) {
+ size_t nodes = 1;
+
+ while (node != NULL) {
+ if (IS_ROOT(node))
+ break;
+ nodes++;
+ node = PARENT(node);
+ }
+
+ return (nodes);
+}
+
+/*
+ * 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);
+static void
+rehash(dns_rbt_t *rbt, unsigned int newcount);
+#else
+#define hash_node(rbt, node, name)
+#define unhash_node(rbt, node)
+#define rehash(rbt, newcount)
+#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
+addonlevel(dns_rbtnode_t *node, dns_rbtnode_t *current, int order,
+ dns_rbtnode_t **rootp);
+
+static void
+deletefromlevel(dns_rbtnode_t *item, dns_rbtnode_t **rootp);
+
+static isc_result_t
+treefix(dns_rbt_t *rbt, void *base, size_t size,
+ dns_rbtnode_t *n, dns_name_t *name,
+ dns_rbtdatafixer_t datafixer, void *fixer_arg,
+ uint64_t *crc);
+
+static void
+deletetreeflat(dns_rbt_t *rbt, unsigned int quantum, bool unhash,
+ dns_rbtnode_t **nodep);
+
+static void
+printnodename(dns_rbtnode_t *node, bool quoted, FILE *f);
+
+static void
+freenode(dns_rbt_t *rbt, dns_rbtnode_t **nodep);
+
+static isc_result_t
+dns_rbt_zero_header(FILE *file) {
+ /*
+ * Write out a zeroed header as a placeholder. Doing this ensures
+ * that the file will not read while it is partially written, should
+ * writing fail or be interrupted.
+ */
+ char buffer[HEADER_LENGTH];
+ isc_result_t result;
+
+ memset(buffer, 0, HEADER_LENGTH);
+ result = isc_stdio_write(buffer, 1, HEADER_LENGTH, file, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = fflush(file);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_once_t once = ISC_ONCE_INIT;
+
+static void
+init_file_version(void) {
+ int n;
+
+ memset(FILE_VERSION, 0, sizeof(FILE_VERSION));
+ n = snprintf(FILE_VERSION, sizeof(FILE_VERSION),
+ "RBT Image %s %s", dns_major, dns_mapapi);
+ INSIST(n > 0 && (unsigned int)n < sizeof(FILE_VERSION));
+}
+
+/*
+ * Write out the real header, including NodeDump version information
+ * and the offset of the first node.
+ *
+ * Any information stored in the rbt object itself should be stored
+ * here.
+ */
+static isc_result_t
+write_header(FILE *file, dns_rbt_t *rbt, uint64_t first_node_offset,
+ uint64_t crc)
+{
+ file_header_t header;
+ isc_result_t result;
+ off_t location;
+
+ RUNTIME_CHECK(isc_once_do(&once, init_file_version) == ISC_R_SUCCESS);
+
+ memset(&header, 0, sizeof(file_header_t));
+ memmove(header.version1, FILE_VERSION, sizeof(header.version1));
+ memmove(header.version2, FILE_VERSION, sizeof(header.version2));
+ header.first_node_offset = first_node_offset;
+ header.ptrsize = (uint32_t) sizeof(void *);
+ header.bigendian = (1 == htonl(1)) ? 1 : 0;
+
+#ifdef DNS_RDATASET_FIXED
+ header.rdataset_fixed = 1;
+#else
+ header.rdataset_fixed = 0;
+#endif
+
+ header.nodecount = rbt->nodecount;
+
+ header.crc = crc;
+
+ CHECK(isc_stdio_tell(file, &location));
+ location = dns_rbt_serialize_align(location);
+ CHECK(isc_stdio_seek(file, location, SEEK_SET));
+ CHECK(isc_stdio_write(&header, 1, sizeof(file_header_t), file, NULL));
+ CHECK(fflush(file));
+
+ /* Ensure we are always at the end of the file. */
+ CHECK(isc_stdio_seek(file, 0, SEEK_END));
+
+ cleanup:
+ return (result);
+}
+
+static bool
+match_header_version(file_header_t *header) {
+ RUNTIME_CHECK(isc_once_do(&once, init_file_version) == ISC_R_SUCCESS);
+
+ if (memcmp(header->version1, FILE_VERSION,
+ sizeof(header->version1)) != 0 ||
+ memcmp(header->version2, FILE_VERSION,
+ sizeof(header->version1)) != 0)
+ {
+ return (false);
+ }
+
+ return (true);
+}
+
+static isc_result_t
+serialize_node(FILE *file, dns_rbtnode_t *node, uintptr_t left,
+ uintptr_t right, uintptr_t down, uintptr_t parent,
+ uintptr_t data, uint64_t *crc)
+{
+ dns_rbtnode_t temp_node;
+ off_t file_position;
+ unsigned char *node_data;
+ size_t datasize;
+ isc_result_t result;
+#ifdef DEBUG
+ dns_name_t nodename;
+#endif
+
+ INSIST(node != NULL);
+
+ CHECK(isc_stdio_tell(file, &file_position));
+ file_position = dns_rbt_serialize_align(file_position);
+ CHECK(isc_stdio_seek(file, file_position, SEEK_SET));
+
+ temp_node = *node;
+ temp_node.down_is_relative = 0;
+ temp_node.left_is_relative = 0;
+ temp_node.right_is_relative = 0;
+ temp_node.parent_is_relative = 0;
+ temp_node.data_is_relative = 0;
+ temp_node.is_mmapped = 1;
+
+ /*
+ * If the next node is not NULL, calculate the next node's location
+ * in the file. Note that this will have to change when the data
+ * structure changes, and it also assumes that we always write the
+ * nodes out in list order (which we currently do.)
+ */
+ if (temp_node.parent != NULL) {
+ temp_node.parent = (dns_rbtnode_t *)(parent);
+ temp_node.parent_is_relative = 1;
+ }
+ if (temp_node.left != NULL) {
+ temp_node.left = (dns_rbtnode_t *)(left);
+ temp_node.left_is_relative = 1;
+ }
+ if (temp_node.right != NULL) {
+ temp_node.right = (dns_rbtnode_t *)(right);
+ temp_node.right_is_relative = 1;
+ }
+ if (temp_node.down != NULL) {
+ temp_node.down = (dns_rbtnode_t *)(down);
+ temp_node.down_is_relative = 1;
+ }
+ if (temp_node.data != NULL) {
+ temp_node.data = (dns_rbtnode_t *)(data);
+ temp_node.data_is_relative = 1;
+ }
+
+ node_data = (unsigned char *) node + sizeof(dns_rbtnode_t);
+ datasize = NODE_SIZE(node) - sizeof(dns_rbtnode_t);
+
+ CHECK(isc_stdio_write(&temp_node, 1, sizeof(dns_rbtnode_t),
+ file, NULL));
+ CHECK(isc_stdio_write(node_data, 1, datasize, file, NULL));
+
+#ifdef DEBUG
+ dns_name_init(&nodename, NULL);
+ NODENAME(node, &nodename);
+ fprintf(stderr, "serialize ");
+ dns_name_print(&nodename, stderr);
+ fprintf(stderr, "\n");
+ hexdump("node header", (unsigned char*) &temp_node,
+ sizeof(dns_rbtnode_t));
+ hexdump("node data", node_data, datasize);
+#endif
+
+ isc_crc64_update(crc, (const uint8_t *) &temp_node,
+ sizeof(dns_rbtnode_t));
+ isc_crc64_update(crc, (const uint8_t *) node_data, datasize);
+
+ cleanup:
+ return (result);
+}
+
+static isc_result_t
+serialize_nodes(FILE *file, dns_rbtnode_t *node, uintptr_t parent,
+ dns_rbtdatawriter_t datawriter, void *writer_arg,
+ uintptr_t *where, uint64_t *crc)
+{
+ uintptr_t left = 0, right = 0, down = 0, data = 0;
+ off_t location = 0, offset_adjust;
+ isc_result_t result;
+
+ if (node == NULL) {
+ if (where != NULL)
+ *where = 0;
+ return (ISC_R_SUCCESS);
+ }
+
+ /* Reserve space for current node. */
+ CHECK(isc_stdio_tell(file, &location));
+ location = dns_rbt_serialize_align(location);
+ CHECK(isc_stdio_seek(file, location, SEEK_SET));
+
+ offset_adjust = dns_rbt_serialize_align(location + NODE_SIZE(node));
+ CHECK(isc_stdio_seek(file, offset_adjust, SEEK_SET));
+
+ /*
+ * Serialize the rest of the tree.
+ *
+ * WARNING: A change in the order (from left, right, down)
+ * will break the way the crc hash is computed.
+ */
+ CHECK(serialize_nodes(file, getleft(node, NULL), location,
+ datawriter, writer_arg, &left, crc));
+ CHECK(serialize_nodes(file, getright(node, NULL), location,
+ datawriter, writer_arg, &right, crc));
+ CHECK(serialize_nodes(file, getdown(node, NULL), location,
+ datawriter, writer_arg, &down, crc));
+
+ if (node->data != NULL) {
+ off_t ret;
+
+ CHECK(isc_stdio_tell(file, &ret));
+ ret = dns_rbt_serialize_align(ret);
+ CHECK(isc_stdio_seek(file, ret, SEEK_SET));
+ data = ret;
+
+ datawriter(file, node->data, writer_arg, crc);
+ }
+
+ /* Seek back to reserved space. */
+ CHECK(isc_stdio_seek(file, location, SEEK_SET));
+
+ /* Serialize the current node. */
+ CHECK(serialize_node(file, node, left, right, down, parent, data, crc));
+
+ /* Ensure we are always at the end of the file. */
+ CHECK(isc_stdio_seek(file, 0, SEEK_END));
+
+ if (where != NULL)
+ *where = (uintptr_t) location;
+
+ cleanup:
+ return (result);
+}
+
+off_t
+dns_rbt_serialize_align(off_t target) {
+ off_t offset = target % 8;
+
+ if (offset == 0)
+ return (target);
+ else
+ return (target + 8 - offset);
+}
+
+isc_result_t
+dns_rbt_serialize_tree(FILE *file, dns_rbt_t *rbt,
+ dns_rbtdatawriter_t datawriter,
+ void *writer_arg, off_t *offset)
+{
+ isc_result_t result;
+ off_t header_position, node_position, end_position;
+ uint64_t crc;
+
+ REQUIRE(file != NULL);
+
+ CHECK(isc_file_isplainfilefd(fileno(file)));
+
+ isc_crc64_init(&crc);
+
+ CHECK(isc_stdio_tell(file, &header_position));
+
+ /* Write dummy header */
+ CHECK(dns_rbt_zero_header(file));
+
+ /* Serialize nodes */
+ CHECK(isc_stdio_tell(file, &node_position));
+ CHECK(serialize_nodes(file, rbt->root, 0, datawriter,
+ writer_arg, NULL, &crc));
+
+ CHECK(isc_stdio_tell(file, &end_position));
+ if (node_position == end_position) {
+ CHECK(isc_stdio_seek(file, header_position, SEEK_SET));
+ *offset = 0;
+ return (ISC_R_SUCCESS);
+ }
+
+ isc_crc64_final(&crc);
+#ifdef DEBUG
+ hexdump("serializing CRC", (unsigned char *)&crc, sizeof(crc));
+#endif
+
+ /* Serialize header */
+ CHECK(isc_stdio_seek(file, header_position, SEEK_SET));
+ CHECK(write_header(file, rbt, HEADER_LENGTH, crc));
+
+ /* Ensure we are always at the end of the file. */
+ CHECK(isc_stdio_seek(file, 0, SEEK_END));
+ *offset = dns_rbt_serialize_align(header_position);
+
+ cleanup:
+ return (result);
+}
+
+#define CONFIRM(a) do { \
+ if (! (a)) { \
+ result = ISC_R_INVALIDFILE; \
+ goto cleanup; \
+ } \
+} while(0);
+
+static isc_result_t
+treefix(dns_rbt_t *rbt, void *base, size_t filesize, dns_rbtnode_t *n,
+ dns_name_t *name, dns_rbtdatafixer_t datafixer,
+ void *fixer_arg, uint64_t *crc)
+{
+ isc_result_t result = ISC_R_SUCCESS;
+ dns_fixedname_t fixed;
+ dns_name_t nodename, *fullname;
+ unsigned char *node_data;
+ dns_rbtnode_t header;
+ size_t datasize, nodemax = filesize - sizeof(dns_rbtnode_t);
+
+ if (n == NULL)
+ return (ISC_R_SUCCESS);
+
+ CONFIRM((void *) n >= base);
+ CONFIRM((char *) n - (char *) base <= (int) nodemax);
+ CONFIRM(DNS_RBTNODE_VALID(n));
+
+ dns_name_init(&nodename, NULL);
+ NODENAME(n, &nodename);
+
+ fullname = &nodename;
+ CONFIRM(dns_name_isvalid(fullname));
+
+ if (!dns_name_isabsolute(&nodename)) {
+ fullname = dns_fixedname_initname(&fixed);
+ CHECK(dns_name_concatenate(&nodename, name, fullname, NULL));
+ }
+
+ /* memorize header contents prior to fixup */
+ memmove(&header, n, sizeof(header));
+
+ if (n->left_is_relative) {
+ CONFIRM(n->left <= (dns_rbtnode_t *) nodemax);
+ n->left = getleft(n, rbt->mmap_location);
+ n->left_is_relative = 0;
+ CONFIRM(DNS_RBTNODE_VALID(n->left));
+ } else
+ CONFIRM(n->left == NULL);
+
+ if (n->right_is_relative) {
+ CONFIRM(n->right <= (dns_rbtnode_t *) nodemax);
+ n->right = getright(n, rbt->mmap_location);
+ n->right_is_relative = 0;
+ CONFIRM(DNS_RBTNODE_VALID(n->right));
+ } else
+ CONFIRM(n->right == NULL);
+
+ if (n->down_is_relative) {
+ CONFIRM(n->down <= (dns_rbtnode_t *) nodemax);
+ n->down = getdown(n, rbt->mmap_location);
+ n->down_is_relative = 0;
+ CONFIRM(n->down > (dns_rbtnode_t *) n);
+ CONFIRM(DNS_RBTNODE_VALID(n->down));
+ } else
+ CONFIRM(n->down == NULL);
+
+ if (n->parent_is_relative) {
+ CONFIRM(n->parent <= (dns_rbtnode_t *) nodemax);
+ n->parent = getparent(n, rbt->mmap_location);
+ n->parent_is_relative = 0;
+ CONFIRM(n->parent < (dns_rbtnode_t *) n);
+ CONFIRM(DNS_RBTNODE_VALID(n->parent));
+ } else
+ CONFIRM(n->parent == NULL);
+
+ if (n->data_is_relative) {
+ CONFIRM(n->data <= (void *) filesize);
+ n->data = getdata(n, rbt->mmap_location);
+ n->data_is_relative = 0;
+ CONFIRM(n->data > (void *) n);
+ } else
+ CONFIRM(n->data == NULL);
+
+ hash_node(rbt, n, fullname);
+
+ /* a change in the order (from left, right, down) will break hashing*/
+ if (n->left != NULL)
+ CHECK(treefix(rbt, base, filesize, n->left, name,
+ datafixer, fixer_arg, crc));
+ if (n->right != NULL)
+ CHECK(treefix(rbt, base, filesize, n->right, name,
+ datafixer, fixer_arg, crc));
+ if (n->down != NULL)
+ CHECK(treefix(rbt, base, filesize, n->down, fullname,
+ datafixer, fixer_arg, crc));
+
+ if (datafixer != NULL && n->data != NULL)
+ CHECK(datafixer(n, base, filesize, fixer_arg, crc));
+
+ rbt->nodecount++;
+ node_data = (unsigned char *) n + sizeof(dns_rbtnode_t);
+ datasize = NODE_SIZE(n) - sizeof(dns_rbtnode_t);
+
+#ifdef DEBUG
+ fprintf(stderr, "deserialize ");
+ dns_name_print(&nodename, stderr);
+ fprintf(stderr, "\n");
+ hexdump("node header", (unsigned char *) &header,
+ sizeof(dns_rbtnode_t));
+ hexdump("node data", node_data, datasize);
+#endif
+ isc_crc64_update(crc, (const uint8_t *) &header,
+ sizeof(dns_rbtnode_t));
+ isc_crc64_update(crc, (const uint8_t *) node_data,
+ datasize);
+
+ cleanup:
+ return (result);
+}
+
+isc_result_t
+dns_rbt_deserialize_tree(void *base_address, size_t filesize,
+ off_t header_offset, isc_mem_t *mctx,
+ dns_rbtdeleter_t deleter, void *deleter_arg,
+ dns_rbtdatafixer_t datafixer, void *fixer_arg,
+ dns_rbtnode_t **originp, dns_rbt_t **rbtp)
+{
+ isc_result_t result = ISC_R_SUCCESS;
+ file_header_t *header;
+ dns_rbt_t *rbt = NULL;
+ uint64_t crc;
+ unsigned int host_big_endian;
+
+ REQUIRE(originp == NULL || *originp == NULL);
+ REQUIRE(rbtp != NULL && *rbtp == NULL);
+
+ isc_crc64_init(&crc);
+
+ CHECK(dns_rbt_create(mctx, deleter, deleter_arg, &rbt));
+
+ rbt->mmap_location = base_address;
+
+ header = (file_header_t *)((char *)base_address + header_offset);
+ if (!match_header_version(header)) {
+ result = ISC_R_INVALIDFILE;
+ goto cleanup;
+ }
+
+#ifdef DNS_RDATASET_FIXED
+ if (header->rdataset_fixed != 1) {
+ result = ISC_R_INVALIDFILE;
+ goto cleanup;
+ }
+
+#else
+ if (header->rdataset_fixed != 0) {
+ result = ISC_R_INVALIDFILE;
+ goto cleanup;
+ }
+#endif
+
+ if (header->ptrsize != (uint32_t) sizeof(void *)) {
+ result = ISC_R_INVALIDFILE;
+ goto cleanup;
+ }
+
+ host_big_endian = (1 == htonl(1));
+ if (header->bigendian != host_big_endian) {
+ result = ISC_R_INVALIDFILE;
+ goto cleanup;
+ }
+
+ /* Copy other data items from the header into our rbt. */
+ rbt->root = (dns_rbtnode_t *)((char *)base_address +
+ header_offset + header->first_node_offset);
+
+ if ((header->nodecount * sizeof(dns_rbtnode_t)) > filesize) {
+ result = ISC_R_INVALIDFILE;
+ goto cleanup;
+ }
+ rehash(rbt, header->nodecount);
+
+ CHECK(treefix(rbt, base_address, filesize, rbt->root,
+ dns_rootname, datafixer, fixer_arg, &crc));
+
+ isc_crc64_final(&crc);
+#ifdef DEBUG
+ hexdump("deserializing CRC", (unsigned char *)&crc, sizeof(crc));
+#endif
+
+ /* Check file hash */
+ if (header->crc != crc) {
+ result = ISC_R_INVALIDFILE;
+ goto cleanup;
+ }
+
+ if (header->nodecount != rbt->nodecount) {
+ result = ISC_R_INVALIDFILE;
+ goto cleanup;
+ }
+
+#ifdef DNS_RBT_USEHASH
+ fixup_uppernodes(rbt);
+#endif /* DNS_RBT_USEHASH */
+
+ *rbtp = rbt;
+ if (originp != NULL)
+ *originp = rbt->root;
+
+ cleanup:
+ if (result != ISC_R_SUCCESS && rbt != NULL) {
+ rbt->root = NULL;
+ rbt->nodecount = 0;
+ dns_rbt_destroy(&rbt);
+ }
+
+ return (result);
+}
+
+/*
+ * Initialize a red/black tree of trees.
+ */
+isc_result_t
+dns_rbt_create(isc_mem_t *mctx, dns_rbtdeleter_t deleter,
+ 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 = NULL;
+ isc_mem_attach(mctx, &rbt->mctx);
+ rbt->data_deleter = deleter;
+ rbt->deleter_arg = deleter_arg;
+ rbt->root = NULL;
+ rbt->nodecount = 0;
+ rbt->hashtable = NULL;
+ rbt->hashsize = 0;
+ rbt->mmap_location = NULL;
+
+#ifdef DNS_RBT_USEHASH
+ result = inithash(rbt);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_putanddetach(&rbt->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;
+
+ deletetreeflat(rbt, quantum, false, &rbt->root);
+ if (rbt->root != NULL)
+ return (ISC_R_QUOTA);
+
+ INSIST(rbt->nodecount == 0);
+
+ rbt->mmap_location = NULL;
+
+ if (rbt->hashtable != NULL)
+ isc_mem_put(rbt->mctx, rbt->hashtable,
+ rbt->hashsize * sizeof(dns_rbtnode_t *));
+
+ rbt->magic = 0;
+
+ isc_mem_putanddetach(&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);
+}
+
+size_t
+dns_rbt_hashsize(dns_rbt_t *rbt) {
+
+ REQUIRE(VALID_RBT(rbt));
+
+ return (rbt->hashsize);
+}
+
+static inline isc_result_t
+chain_name(dns_rbtnodechain_t *chain, dns_name_t *name,
+ bool 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;
+ unsigned int level_count;
+ unsigned int common_labels;
+ unsigned int nlabels, hlabels;
+ int order;
+
+ REQUIRE(VALID_RBT(rbt));
+ REQUIRE(dns_name_isabsolute(name));
+ REQUIRE(nodep != NULL && *nodep == NULL);
+
+ /*
+ * Dear future BIND developer,
+ *
+ * After you have tried attempting to optimize this routine by
+ * using the hashtable and have realized your folly, please
+ * append another cross ("X") below as a warning to the next
+ * future BIND developer:
+ *
+ * Number of victim developers: X
+ *
+ * I wish the past developer had included such a notice.
+ *
+ * Long form: Unlike dns_rbt_findnode(), this function does not
+ * lend itself to be optimized using the hashtable:
+ *
+ * 1. In the subtree where the insertion occurs, this function
+ * needs to have the insertion point and the order where the
+ * lookup terminated (i.e., at the insertion point where left or
+ * right child is NULL). This cannot be determined from the
+ * hashtable, so at least in that subtree, a BST O(log N) lookup
+ * is necessary.
+ *
+ * 2. Our RBT nodes contain not only single labels but label
+ * sequences to optimize space usage. So at every level, we have
+ * to look for a match in the hashtable for all superdomains in
+ * the rest of the name we're searching. This is an O(N)
+ * operation at least, here N being the label size of name, each
+ * of which is a hashtable lookup involving dns_name_equal()
+ * comparisons.
+ */
+
+ /*
+ * Create a copy of the name so the original name structure is
+ * not modified.
+ */
+ add_name = dns_fixedname_initname(&fixedcopy);
+ dns_name_clone(name, add_name);
+
+ if (ISC_UNLIKELY(rbt->root == NULL)) {
+ result = create_node(rbt->mctx, add_name, &new_current);
+ if (result == ISC_R_SUCCESS) {
+ rbt->nodecount++;
+ new_current->is_root = 1;
+#ifdef DNS_RBT_USEHASH
+ UPPERNODE(new_current) = NULL;
+#endif /* DNS_RBT_USEHASH */
+ rbt->root = new_current;
+ *nodep = new_current;
+ hash_node(rbt, new_current, name);
+ }
+ return (result);
+ }
+
+ level_count = 0;
+
+ prefix = dns_fixedname_initname(&fixedprefix);
+ suffix = dns_fixedname_initname(&fixedsuffix);
+
+ root = &rbt->root;
+ INSIST(IS_ROOT(*root));
+ parent = NULL;
+ current = NULL;
+ child = *root;
+ dns_name_init(&current_name, current_offsets);
+ new_name = dns_fixedname_initname(&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);
+
+ INSIST(level_count < DNS_RBT_LEVELBLOCK);
+ level_count++;
+ } 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?
+ */
+ if (level_count >= DNS_RBT_LEVELBLOCK) {
+ 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;
+ if (current->nsec == DNS_RBT_NSEC_HAS_NSEC)
+ new_current->nsec = DNS_RBT_NSEC_NORMAL;
+ else
+ new_current->nsec = current->nsec;
+ 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;
+
+ /*
+ * 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);
+#ifdef DNS_RBT_USEHASH
+ UPPERNODE(new_current) = UPPERNODE(current);
+ UPPERNODE(current) = new_current;
+#endif /* DNS_RBT_USEHASH */
+
+ INSIST(level_count < DNS_RBT_LEVELBLOCK);
+ level_count++;
+
+ 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 (ISC_LIKELY(child != NULL));
+
+ if (ISC_LIKELY(result == ISC_R_SUCCESS))
+ result = create_node(rbt->mctx, add_name, &new_current);
+
+ if (ISC_LIKELY(result == ISC_R_SUCCESS)) {
+#ifdef DNS_RBT_USEHASH
+ if (*root == NULL)
+ UPPERNODE(new_current) = current;
+ else
+ UPPERNODE(new_current) = PARENT(*root);
+#endif /* DNS_RBT_USEHASH */
+ 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, const 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;
+ 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 (ISC_UNLIKELY(rbt->root == NULL))
+ return (ISC_R_NOTFOUND);
+
+ /*
+ * Appease GCC about variables it incorrectly thinks are
+ * possibly used uninitialized.
+ */
+ compared = dns_namereln_none;
+ last_compared = NULL;
+ order = 0;
+
+ callback_name = dns_fixedname_initname(&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.
+ */
+ search_name = dns_fixedname_initname(&fixedsearchname);
+ dns_name_clone(name, search_name);
+
+ dns_name_init(&current_name, NULL);
+
+ saved_result = ISC_R_SUCCESS;
+ current = rbt->root;
+
+ while (ISC_LIKELY(current != NULL)) {
+ NODENAME(current, &current_name);
+ compared = dns_name_fullcompare(search_name, &current_name,
+ &order, &common_labels);
+ /*
+ * last_compared is used as a shortcut to start (or
+ * continue rather) finding the stop-node of the search
+ * when hashing was used (see much below in this
+ * function).
+ */
+ last_compared = current;
+
+ if (compared == dns_namereln_equal)
+ break;
+
+ if (compared == dns_namereln_none) {
+#ifdef DNS_RBT_USEHASH
+ /*
+ * Here, current is pointing at a subtree root
+ * node. We try to find a matching node using
+ * the hashtable. We can get one of 3 results
+ * here: (a) we locate the matching node, (b) we
+ * find a node to which the current node has a
+ * subdomain relation, (c) we fail to find (a)
+ * or (b).
+ */
+
+ dns_name_t hash_name;
+ dns_rbtnode_t *hnode;
+ dns_rbtnode_t *up_current;
+ unsigned int nlabels;
+ unsigned int tlabels = 1;
+ unsigned int hash;
+
+ /*
+ * The case of current not being a subtree 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(IS_ROOT(current));
+
+ nlabels = dns_name_countlabels(search_name);
+
+ /*
+ * current is the root of the current level, so
+ * its parent is the same as its "up" pointer.
+ */
+ up_current = PARENT(current);
+ dns_name_init(&hash_name, NULL);
+
+ hashagain:
+ /*
+ * Compute the hash over the full absolute
+ * name. Look for the smallest suffix match at
+ * this tree level (hlevel), and then at every
+ * iteration, look for the next smallest suffix
+ * match (add another subdomain label to the
+ * absolute name being hashed).
+ */
+ dns_name_getlabelsequence(name,
+ nlabels - tlabels,
+ hlabels + tlabels,
+ &hash_name);
+ hash = dns_name_fullhash(&hash_name, false);
+ dns_name_getlabelsequence(search_name,
+ nlabels - tlabels,
+ tlabels, &hash_name);
+
+ /*
+ * Walk all the nodes in the hash bucket pointed
+ * by the computed hash value.
+ */
+ for (hnode = rbt->hashtable[hash % rbt->hashsize];
+ hnode != NULL;
+ hnode = hnode->hashnext)
+ {
+ dns_name_t hnode_name;
+
+ if (ISC_LIKELY(hash != HASHVAL(hnode)))
+ continue;
+ /*
+ * This checks that the hashed label
+ * sequence being looked up is at the
+ * same tree level, so that we don't
+ * match a labelsequence from some other
+ * subdomain.
+ */
+ if (ISC_LIKELY(get_upper_node(hnode) != up_current))
+ continue;
+
+ dns_name_init(&hnode_name, NULL);
+ NODENAME(hnode, &hnode_name);
+ if (ISC_LIKELY(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;
+
+#else /* DNS_RBT_USEHASH */
+
+ /*
+ * Standard binary search tree movement.
+ */
+ if (order < 0)
+ current = LEFT(current);
+ else
+ current = RIGHT(current);
+
+#endif /* DNS_RBT_USEHASH */
+
+ } 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) {
+#ifdef DNS_RBT_USEHASH
+ subdomain:
+#endif
+ /*
+ * 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,
+ 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);
+ } 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, 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,
+ 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);
+ POST(compared);
+
+ 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, const 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, bool 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, bool recurse)
+{
+ dns_rbtnode_t *parent;
+
+ REQUIRE(VALID_RBT(rbt));
+ REQUIRE(DNS_RBTNODE_VALID(node));
+ INSIST(rbt->nodecount != 0);
+
+ if (DOWN(node) != NULL) {
+ if (recurse) {
+ PARENT(DOWN(node)) = NULL;
+ deletetreeflat(rbt, 0, true, &DOWN(node));
+ } 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).
+ * But join_nodes() function has now been removed.
+ */
+ 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 = get_upper_node(node);
+
+ /*
+ * This node now has no down pointer, so now it needs
+ * to be removed from this level.
+ */
+ 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);
+
+ freenode(rbt, &node);
+
+ /*
+ * 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 = get_upper_node(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);
+
+ name = dns_fixedname_initname(&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;
+ size_t nodelen;
+
+ 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.
+ */
+ nodelen = sizeof(dns_rbtnode_t) + region.length + labels + 1;
+ node = (dns_rbtnode_t *)isc_mem_get(mctx, nodelen);
+ if (node == NULL)
+ return (ISC_R_NOMEMORY);
+ memset(node, 0, nodelen);
+
+ node->is_root = 0;
+ PARENT(node) = NULL;
+ RIGHT(node) = NULL;
+ LEFT(node) = NULL;
+ DOWN(node) = NULL;
+ DATA(node) = NULL;
+ node->is_mmapped = 0;
+ node->down_is_relative = 0;
+ node->left_is_relative = 0;
+ node->right_is_relative = 0;
+ node->parent_is_relative = 0;
+ node->data_is_relative = 0;
+ node->rpz = 0;
+
+#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->nsec = DNS_RBT_NSEC_NORMAL;
+
+ 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.
+ *
+ * Note: OLDOFFSETLEN *must* be assigned *after* OLDNAMELEN is assigned
+ * as it uses OLDNAMELEN.
+ */
+ OLDNAMELEN(node) = NAMELEN(node) = region.length;
+ OLDOFFSETLEN(node) = OFFSETLEN(node) = labels;
+ ATTRS(node) = name->attributes;
+
+ memmove(NAME(node), region.base, region.length);
+ memmove(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;
+
+ REQUIRE(name != NULL);
+
+ HASHVAL(node) = dns_name_fullhash(name, 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 = (unsigned int)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 newcount) {
+ unsigned int oldsize;
+ dns_rbtnode_t **oldtable;
+ dns_rbtnode_t *node;
+ dns_rbtnode_t *nextnode;
+ unsigned int hash;
+ unsigned int i;
+
+ oldsize = (unsigned int)rbt->hashsize;
+ oldtable = rbt->hashtable;
+ do {
+ INSIST((rbt->hashsize * 2 + 1) > rbt->hashsize);
+ rbt->hashsize = rbt->hashsize * 2 + 1;
+ } while (newcount >= (rbt->hashsize * 3));
+ 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++) {
+ for (node = oldtable[i]; node != NULL; node = nextnode) {
+ hash = HASHVAL(node) % rbt->hashsize;
+ nextnode = HASHNEXT(node);
+ HASHNEXT(node) = rbt->hashtable[hash];
+ rbt->hashtable[hash] = node;
+ }
+ }
+
+ 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, rbt->nodecount);
+
+ 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));
+
+ 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;
+
+ 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;
+
+ 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
+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;
+ POST(child);
+
+ 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
+deletefromlevel(dns_rbtnode_t *item, dns_rbtnode_t **rootp) {
+ dns_rbtnode_t *child, *sibling, *parent;
+ dns_rbtnode_t *successor;
+
+ REQUIRE(item != NULL);
+
+ /*
+ * Verify that the parent history is (apparently) correct.
+ */
+ INSIST((IS_ROOT(item) && *rootp == item) ||
+ (! IS_ROOT(item) &&
+ (LEFT(PARENT(item)) == item ||
+ RIGHT(PARENT(item)) == item)));
+
+ child = NULL;
+
+ if (LEFT(item) == NULL) {
+ if (RIGHT(item) == NULL) {
+ if (IS_ROOT(item)) {
+ /*
+ * This is the only item in the tree.
+ */
+ *rootp = NULL;
+ return;
+ }
+ } else
+ /*
+ * This node has one child, on the right.
+ */
+ child = RIGHT(item);
+
+ } else if (RIGHT(item) == NULL)
+ /*
+ * This node has one child, on the left.
+ */
+ child = LEFT(item);
+ 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(item);
+ 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.
+ */
+ memmove(tmp, successor, sizeof(dns_rbtnode_t));
+
+ if (IS_ROOT(item)) {
+ *rootp = successor;
+ successor->is_root = true;
+ item->is_root = false;
+
+ } else
+ if (LEFT(PARENT(item)) == item)
+ LEFT(PARENT(item)) = successor;
+ else
+ RIGHT(PARENT(item)) = successor;
+
+ PARENT(successor) = PARENT(item);
+ LEFT(successor) = LEFT(item);
+ RIGHT(successor) = RIGHT(item);
+ COLOR(successor) = COLOR(item);
+
+ 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(item));
+
+ if (PARENT(tmp) == item) {
+ /*
+ * Node being deleted was successor's parent.
+ */
+ RIGHT(successor) = item;
+ PARENT(item) = successor;
+
+ } else {
+ LEFT(PARENT(tmp)) = item;
+ PARENT(item) = PARENT(tmp);
+ }
+
+ /*
+ * Original location of successor node has no left.
+ */
+ LEFT(item) = NULL;
+ RIGHT(item) = RIGHT(tmp);
+ COLOR(item) = COLOR(tmp);
+ }
+
+ /*
+ * Remove the node by removing the links from its parent.
+ */
+ if (! IS_ROOT(item)) {
+ if (LEFT(PARENT(item)) == item)
+ LEFT(PARENT(item)) = child;
+ else
+ RIGHT(PARENT(item)) = child;
+
+ if (child != NULL)
+ PARENT(child) = PARENT(item);
+
+ } 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(item);
+ }
+
+ /*
+ * Fix color violations.
+ */
+ if (IS_BLACK(item)) {
+ parent = PARENT(item);
+
+ 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);
+ }
+
+ INSIST(sibling != NULL);
+
+ 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);
+ INSIST(RIGHT(sibling) != NULL);
+ MAKE_BLACK(RIGHT(sibling));
+ rotate_left(parent, rootp);
+ child = *rootp;
+ }
+
+ } else {
+ /*
+ * Child is parent's right child.
+ * Everything is done 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);
+ }
+
+ INSIST(sibling != NULL);
+
+ 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);
+ INSIST(LEFT(sibling) != NULL);
+ MAKE_BLACK(LEFT(sibling));
+ rotate_right(parent, rootp);
+ child = *rootp;
+ }
+ }
+
+ parent = PARENT(child);
+ }
+
+ if (IS_RED(child))
+ MAKE_BLACK(child);
+ }
+}
+
+static void
+freenode(dns_rbt_t *rbt, dns_rbtnode_t **nodep) {
+ dns_rbtnode_t *node = *nodep;
+
+ if (node->is_mmapped == 0) {
+ isc_mem_put(rbt->mctx, node, NODE_SIZE(node));
+ }
+ *nodep = NULL;
+
+ rbt->nodecount--;
+}
+
+static void
+deletetreeflat(dns_rbt_t *rbt, unsigned int quantum, bool unhash,
+ dns_rbtnode_t **nodep)
+{
+ dns_rbtnode_t *root = *nodep;
+
+ while (root != NULL) {
+ /*
+ * If there is a left, right or down node, walk into it
+ * and iterate.
+ */
+ if (LEFT(root) != NULL) {
+ dns_rbtnode_t *node = root;
+ root = LEFT(root);
+ LEFT(node) = NULL;
+ } else if (RIGHT(root) != NULL) {
+ dns_rbtnode_t *node = root;
+ root = RIGHT(root);
+ RIGHT(node) = NULL;
+ } else if (DOWN(root) != NULL) {
+ dns_rbtnode_t *node = root;
+ root = DOWN(root);
+ DOWN(node) = NULL;
+ } else {
+ /*
+ * There are no left, right or down nodes, so we
+ * can free this one and go back to its parent.
+ */
+ dns_rbtnode_t *node = root;
+ root = PARENT(root);
+
+ if (DATA(node) != NULL && rbt->data_deleter != NULL)
+ rbt->data_deleter(DATA(node),
+ rbt->deleter_arg);
+ if (unhash)
+ unhash_node(rbt, node);
+ /*
+ * Note: we don't call unhash_node() here as we
+ * are destroying the complete RBT tree.
+ */
+#if DNS_RBT_USEMAGIC
+ node->magic = 0;
+#endif
+ freenode(rbt, &node);
+ if (quantum != 0 && --quantum == 0)
+ break;
+ }
+ }
+
+ *nodep = root;
+}
+
+static size_t
+getheight_helper(dns_rbtnode_t *node) {
+ size_t dl, dr;
+ size_t this_height, down_height;
+
+ if (node == NULL)
+ return (0);
+
+ dl = getheight_helper(LEFT(node));
+ dr = getheight_helper(RIGHT(node));
+
+ this_height = ISC_MAX(dl + 1, dr + 1);
+ down_height = getheight_helper(DOWN(node));
+
+ return (ISC_MAX(this_height, down_height));
+}
+
+size_t
+dns__rbt_getheight(dns_rbt_t *rbt) {
+ return (getheight_helper(rbt->root));
+}
+
+static bool
+check_properties_helper(dns_rbtnode_t *node) {
+ if (node == NULL)
+ return (true);
+
+ if (IS_RED(node)) {
+ /* Root nodes must be BLACK. */
+ if (IS_ROOT(node))
+ return (false);
+
+ /* Both children of RED nodes must be BLACK. */
+ if (IS_RED(LEFT(node)) || IS_RED(RIGHT(node)))
+ return (false);
+ }
+
+ if ((DOWN(node) != NULL) && (!IS_ROOT(DOWN(node))))
+ return (false);
+
+ if (IS_ROOT(node)) {
+ if ((PARENT(node) != NULL) &&
+ (DOWN(PARENT(node)) != node))
+ return (false);
+
+ if (get_upper_node(node) != PARENT(node))
+ return (false);
+ }
+
+ /* If node is assigned to the down_ pointer of its parent, it is
+ * a subtree root and must have the flag set.
+ */
+ if (((!PARENT(node)) ||
+ (DOWN(PARENT(node)) == node)) &&
+ (!IS_ROOT(node)))
+ {
+ return (false);
+ }
+
+ /* Repeat tests with this node's children. */
+ return (check_properties_helper(LEFT(node)) &&
+ check_properties_helper(RIGHT(node)) &&
+ check_properties_helper(DOWN(node)));
+}
+
+static bool
+check_black_distance_helper(dns_rbtnode_t *node, size_t *distance) {
+ size_t dl, dr, dd;
+
+ if (node == NULL) {
+ *distance = 1;
+ return (true);
+ }
+
+ if (!check_black_distance_helper(LEFT(node), &dl))
+ return (false);
+
+ if (!check_black_distance_helper(RIGHT(node), &dr))
+ return (false);
+
+ if (!check_black_distance_helper(DOWN(node), &dd))
+ return (false);
+
+ /* Left and right side black node counts must match. */
+ if (dl != dr)
+ return (false);
+
+ if (IS_BLACK(node))
+ dl++;
+
+ *distance = dl;
+
+ return (true);
+}
+
+bool
+dns__rbt_checkproperties(dns_rbt_t *rbt) {
+ size_t dd;
+
+ if (!check_properties_helper(rbt->root))
+ return (false);
+
+ /* Path from a given node to all its leaves must contain the
+ * same number of BLACK child nodes. This is done separately
+ * here instead of inside check_properties_helper() as
+ * it would take (n log n) complexity otherwise.
+ */
+ return (check_black_distance_helper(rbt->root, &dd));
+}
+
+static void
+dns_rbt_indent(FILE *f, int depth) {
+ int i;
+
+ fprintf(f, "%4d ", depth);
+
+ for (i = 0; i < depth; i++)
+ fprintf(f, "- ");
+}
+
+void
+dns_rbt_printnodeinfo(dns_rbtnode_t *n, FILE *f) {
+ fprintf(f, "Node info for nodename: ");
+ printnodename(n, true, f);
+ fprintf(f, "\n");
+
+ fprintf(f, "n = %p\n", n);
+
+ fprintf(f, "Relative pointers: %s%s%s%s%s\n",
+ (n->parent_is_relative == 1 ? " P" : ""),
+ (n->right_is_relative == 1 ? " R" : ""),
+ (n->left_is_relative == 1 ? " L" : ""),
+ (n->down_is_relative == 1 ? " D" : ""),
+ (n->data_is_relative == 1 ? " T" : ""));
+
+ fprintf(f, "node lock address = %u\n", n->locknum);
+
+ fprintf(f, "Parent: %p\n", n->parent);
+ fprintf(f, "Right: %p\n", n->right);
+ fprintf(f, "Left: %p\n", n->left);
+ fprintf(f, "Down: %p\n", n->down);
+ fprintf(f, "daTa: %p\n", n->data);
+}
+
+static void
+printnodename(dns_rbtnode_t *node, bool quoted, FILE *f) {
+ 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));
+
+ if (quoted)
+ fprintf(f, "\"%s\"", buffer);
+ else
+ fprintf(f, "%s", buffer);
+}
+
+static void
+print_text_helper(dns_rbtnode_t *root, dns_rbtnode_t *parent,
+ int depth, const char *direction,
+ void (*data_printer)(FILE *, void *), FILE *f)
+{
+ dns_rbt_indent(f, depth);
+
+ if (root != NULL) {
+ printnodename(root, true, f);
+ fprintf(f, " (%s, %s", direction,
+ IS_RED(root) ? "RED" : "BLACK");
+
+ if ((! IS_ROOT(root) && PARENT(root) != parent) ||
+ ( IS_ROOT(root) && depth > 0 &&
+ DOWN(PARENT(root)) != root)) {
+
+ fprintf(f, " (BAD parent pointer! -> ");
+ if (PARENT(root) != NULL)
+ printnodename(PARENT(root), true, f);
+ else
+ fprintf(f, "NULL");
+ fprintf(f, ")");
+ }
+
+ fprintf(f, ")");
+
+ if (root->data != NULL && data_printer != NULL) {
+ fprintf(f, " data@%p: ", root->data);
+ data_printer(f, root->data);
+ }
+ fprintf(f, "\n");
+
+ depth++;
+
+ if (IS_RED(root) && IS_RED(LEFT(root)))
+ fprintf(f, "** Red/Red color violation on left\n");
+ print_text_helper(LEFT(root), root, depth, "left",
+ data_printer, f);
+
+ if (IS_RED(root) && IS_RED(RIGHT(root)))
+ fprintf(f, "** Red/Red color violation on right\n");
+ print_text_helper(RIGHT(root), root, depth, "right",
+ data_printer, f);
+
+ print_text_helper(DOWN(root), NULL, depth, "down",
+ data_printer, f);
+ } else {
+ fprintf(f, "NULL (%s)\n", direction);
+ }
+}
+
+void
+dns_rbt_printtext(dns_rbt_t *rbt,
+ void (*data_printer)(FILE *, void *), FILE *f)
+{
+ REQUIRE(VALID_RBT(rbt));
+
+ print_text_helper(rbt->root, NULL, 0, "root", data_printer, f);
+}
+
+static int
+print_dot_helper(dns_rbtnode_t *node, unsigned int *nodecount,
+ bool show_pointers, FILE *f)
+{
+ unsigned int l, r, d;
+
+ if (node == NULL)
+ return (0);
+
+ l = print_dot_helper(LEFT(node), nodecount, show_pointers, f);
+ r = print_dot_helper(RIGHT(node), nodecount, show_pointers, f);
+ d = print_dot_helper(DOWN(node), nodecount, show_pointers, f);
+
+ *nodecount += 1;
+
+ fprintf(f, "node%u[label = \"<f0> |<f1> ", *nodecount);
+ printnodename(node, false, f);
+ fprintf(f, "|<f2>");
+
+ if (show_pointers)
+ fprintf(f, "|<f3> n=%p|<f4> p=%p", node, PARENT(node));
+
+ fprintf(f, "\"] [");
+
+ if (IS_RED(node))
+ fprintf(f, "color=red");
+ else
+ fprintf(f, "color=black");
+
+ /* XXXMUKS: verify that IS_ROOT() indicates subtree root and not
+ * forest root.
+ */
+ if (IS_ROOT(node))
+ fprintf(f, ",penwidth=3");
+
+ if (IS_EMPTY(node))
+ fprintf(f, ",style=filled,fillcolor=lightgrey");
+
+ fprintf(f, "];\n");
+
+ if (LEFT(node) != NULL)
+ fprintf(f, "\"node%u\":f0 -> \"node%u\":f1;\n", *nodecount, l);
+
+ if (DOWN(node) != NULL)
+ fprintf(f, "\"node%u\":f1 -> \"node%u\":f1 [penwidth=5];\n",
+ *nodecount, d);
+
+ if (RIGHT(node) != NULL)
+ fprintf(f, "\"node%u\":f2 -> \"node%u\":f1;\n", *nodecount, r);
+
+ return (*nodecount);
+}
+
+void
+dns_rbt_printdot(dns_rbt_t *rbt, bool show_pointers, FILE *f) {
+ unsigned int nodecount = 0;
+
+ REQUIRE(VALID_RBT(rbt));
+
+ fprintf(f, "digraph g {\n");
+ fprintf(f, "node [shape = record,height=.1];\n");
+ print_dot_helper(rbt->root, &nodecount, show_pointers, f);
+ fprintf(f, "}\n");
+}
+
+/*
+ * Chain Functions
+ */
+
+void
+dns_rbtnodechain_init(dns_rbtnodechain_t *chain, isc_mem_t *mctx) {
+
+ REQUIRE(chain != NULL);
+
+ /*
+ * Initialize 'chain'.
+ */
+ 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, 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;
+ bool new_origin = 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 = 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 = 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;
+ bool new_origin = 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 = 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, 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;
+ bool new_origin = 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 = 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
+ * ascends 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) {
+ /*
+ * If the tree we are iterating over
+ * was modified since this chain was
+ * initialized in a way that caused
+ * node splits to occur, "current" may
+ * now be pointing to a root node which
+ * appears to be at level 0, but still
+ * has a parent. If that happens,
+ * abort. Otherwise, we are done
+ * looking for a successor as we really
+ * reached the root node on level 0.
+ */
+ INSIST(PARENT(current) == NULL);
+ break;
+ }
+
+ current = chain->levels[--chain->level_count];
+ new_origin = 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) {
+ /*
+ * If we determine that the current node is the successor to
+ * itself, we will run into an infinite loop, so abort instead.
+ */
+ INSIST(chain->end != successor);
+
+ 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, 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) {
+
+ REQUIRE(VALID_CHAIN(chain));
+
+ /*
+ * Free any dynamic storage associated with 'chain', and then
+ * reinitialize '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;
+}
+
+/* XXXMUKS:
+ *
+ * - worth removing inline as static functions are inlined automatically
+ * where suitable by modern compilers.
+ * - bump the size of dns_rbt.nodecount to size_t.
+ * - the dumpfile header also contains a nodecount that is unsigned
+ * int. If large files (> 2^32 nodes) are to be supported, the
+ * allocation for this field should be increased.
+ */
diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c
new file mode 100644
index 0000000..0861139
--- /dev/null
+++ b/lib/dns/rbtdb.c
@@ -0,0 +1,10431 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+/* #define inline */
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/crc64.h>
+#include <isc/event.h>
+#include <isc/heap.h>
+#include <isc/file.h>
+#include <isc/hex.h>
+#include <isc/mem.h>
+#include <isc/mutex.h>
+#include <isc/once.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/socket.h>
+#include <isc/stdio.h>
+#include <isc/string.h>
+#include <isc/task.h>
+#include <isc/time.h>
+#include <isc/util.h>
+
+#include <dns/acache.h>
+#include <dns/callbacks.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/rpz.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/time.h>
+#include <dns/version.h>
+#include <dns/view.h>
+#include <dns/zone.h>
+#include <dns/zonekey.h>
+
+#ifndef WIN32
+#include <sys/mman.h>
+#else
+#define PROT_READ 0x01
+#define PROT_WRITE 0x02
+#define MAP_PRIVATE 0x0002
+#define MAP_FAILED ((void *)-1)
+#endif
+
+#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
+
+#define CHECK(op) \
+ do { result = (op); \
+ if (result != ISC_R_SUCCESS) goto failure; \
+ } while (0)
+
+/*
+ * This is the map file header for RBTDB images. It is populated, and then
+ * written, as the LAST thing done to the file. Writing this last (with
+ * zeros in the header area initially) will ensure that the header is only
+ * valid when the RBTDB image is also valid.
+ */
+typedef struct rbtdb_file_header rbtdb_file_header_t;
+
+/* Header length, always the same size regardless of structure size */
+#define RBTDB_HEADER_LENGTH 1024
+
+struct rbtdb_file_header {
+ char version1[32];
+ uint32_t ptrsize;
+ unsigned int bigendian:1;
+ uint64_t tree;
+ uint64_t nsec;
+ uint64_t nsec3;
+
+ char version2[32]; /* repeated; must match version1 */
+};
+
+
+/*%
+ * 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 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
+
+#define once once64
+#define FILE_VERSION FILE_VERSION64
+#define init_count init_count64
+
+#define cache_methods cache_methods64
+#define dbiterator_methods dbiterator_methods64
+#define rdataset_methods rdataset_methods64
+#define rdatasetiter_methods rdatasetiter_methods64
+#define slab_methods slab_methods64
+#define zone_methods zone_methods64
+
+#define acache_callback acache_callback64
+#define acache_cancelentry acache_cancelentry64
+#define activeempty activeempty64
+#define activeemtpynode activeemtpynode64
+#define add32 add64
+#define add_changed add_changed64
+#define add_empty_wildcards add_empty_wildcards64
+#define add_wildcard_magic add_wildcard_magic64
+#define addclosest addclosest64
+#define addnoqname addnoqname64
+#define addrdataset addrdataset64
+#define adjust_quantum adjust_quantum64
+#define allocate_version allocate_version64
+#define allrdatasets allrdatasets64
+#define attach attach64
+#define attachnode attachnode64
+#define attachversion attachversion64
+#define beginload beginload64
+#define bind_rdataset bind_rdataset64
+#define cache_find cache_find64
+#define cache_findrdataset cache_findrdataset64
+#define cache_findzonecut cache_findzonecut64
+#define cache_zonecut_callback cache_zonecut_callback64
+#define check_stale_header check_stale_header64
+#define clean_cache_node clean_cache_node64
+#define clean_stale_headers clean_stale_headers64
+#define clean_zone_node clean_zone_node64
+#define cleanup_dead_nodes cleanup_dead_nodes64
+#define cleanup_dead_nodes_callback cleanup_dead_nodes_callback64
+#define cleanup_nondirty cleanup_nondirty64
+#define closeversion closeversion64
+#define cname_and_other_data cname_and_other_data64
+#define createiterator createiterator64
+#define currentversion currentversion64
+#define dbiterator_current dbiterator_current64
+#define dbiterator_destroy dbiterator_destroy64
+#define dbiterator_first dbiterator_first64
+#define dbiterator_last dbiterator_last64
+#define dbiterator_next dbiterator_next64
+#define dbiterator_origin dbiterator_origin64
+#define dbiterator_pause dbiterator_pause64
+#define dbiterator_prev dbiterator_prev64
+#define dbiterator_seek dbiterator_seek64
+#define decrement_reference decrement_reference64
+#define delegating_type delegating_type64
+#define delete_callback delete_callback64
+#define delete_node delete_node64
+#define deleterdataset deleterdataset64
+#define dereference_iter_node dereference_iter_node64
+#define deserialize32 deserialize64
+#define detach detach64
+#define detachnode detachnode64
+#define dump dump64
+#define endload endload64
+#define expire_header expire_header64
+#define expirenode expirenode64
+#define find_closest_nsec find_closest_nsec64
+#define find_coveringnsec find_coveringnsec64
+#define find_deepest_zonecut find_deepest_zonecut64
+#define find_wildcard find_wildcard64
+#define findnode findnode64
+#define findnodeintree findnodeintree64
+#define findnsec3node findnsec3node64
+#define flush_deletions flush_deletions64
+#define free_acachearray free_acachearray64
+#define free_noqname free_noqname64
+#define free_rbtdb free_rbtdb64
+#define free_rbtdb_callback free_rbtdb_callback64
+#define free_rdataset free_rdataset64
+#define getnsec3parameters getnsec3parameters64
+#define getoriginnode getoriginnode64
+#define getrrsetstats getrrsetstats64
+#define getsigningtime getsigningtime64
+#define getsize getsize64
+#define hashsize hashsize64
+#define init_file_version init_file_version64
+#define init_rdataset init_rdataset64
+#define isdnssec isdnssec64
+#define ispersistent ispersistent64
+#define issecure issecure64
+#define iszonesecure iszonesecure64
+#define loading_addrdataset loading_addrdataset64
+#define loadnode loadnode64
+#define make_least_version make_least_version64
+#define mark_stale_header mark_stale_header64
+#define match_header_version match_header_version64
+#define matchparams matchparams64
+#define maybe_free_rbtdb maybe_free_rbtdb64
+#define need_headerupdate need_headerupdate64
+#define new_rdataset new_rdataset64
+#define new_reference new_reference64
+#define newversion newversion64
+#define nodecount nodecount64
+#define nodefullname nodefullname64
+#define overmem overmem64
+#define overmem_purge overmem_purge64
+#define previous_closest_nsec previous_closest_nsec64
+#define printnode printnode64
+#define prune_tree prune_tree64
+#define rbt_datafixer rbt_datafixer64
+#define rbt_datawriter rbt_datawriter64
+#define rbtdb_write_header rbtdb_write_header64
+#define rbtdb_zero_header rbtdb_zero_header64
+#define rdataset_clearprefetch rdataset_clearprefetch64
+#define rdataset_clone rdataset_clone64
+#define rdataset_count rdataset_count64
+#define rdataset_current rdataset_current64
+#define rdataset_disassociate rdataset_disassociate64
+#define rdataset_expire rdataset_expire64
+#define rdataset_first rdataset_first64
+#define rdataset_getadditional rdataset_getadditional64
+#define rdataset_getclosest rdataset_getclosest64
+#define rdataset_getnoqname rdataset_getnoqname64
+#define rdataset_getownercase rdataset_getownercase64
+#define rdataset_next rdataset_next64
+#define rdataset_putadditional rdataset_putadditional64
+#define rdataset_setadditional rdataset_setadditional64
+#define rdataset_setownercase rdataset_setownercase64
+#define rdataset_settrust rdataset_settrust64
+#define rdatasetiter_current rdatasetiter_current64
+#define rdatasetiter_destroy rdatasetiter_destroy64
+#define rdatasetiter_first rdatasetiter_first64
+#define rdatasetiter_next rdatasetiter_next64
+#define reactivate_node reactivate_node64
+#define reference_iter_node reference_iter_node64
+#define resign_delete resign_delete64
+#define resign_insert resign_insert64
+#define resign_sooner resign_sooner64
+#define resigned resigned64
+#define resume_iteration resume_iteration64
+#define rollback_node rollback_node64
+#define rpz_attach rpz_attach64
+#define rpz_ready rpz_ready64
+#define serialize serialize64
+#define set_index set_index64
+#define set_ttl set_ttl64
+#define setcachestats setcachestats64
+#define setnsec3parameters setnsec3parameters64
+#define setownercase setownercase64
+#define setsigningtime setsigningtime64
+#define settask settask64
+#define setup_delegation setup_delegation64
+#define subtractrdataset subtractrdataset64
+#define ttl_sooner ttl_sooner64
+#define update_cachestats update_cachestats64
+#define update_header update_header64
+#define update_newheader update_newheader64
+#define update_recordsandbytes update_recordsandbytes64
+#define update_rrsetstats update_rrsetstats64
+#define valid_glue valid_glue64
+#define zone_find zone_find64
+#define zone_findrdataset zone_findrdataset64
+#define zone_findzonecut zone_findzonecut64
+#define zone_zonecut_callback zone_zonecut_callback64
+
+#else
+typedef uint32_t rbtdb_serial_t;
+#endif
+
+typedef 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(base, ext) ((rbtdb_rdatatype_t)(((uint32_t)ext) << 16) | (((uint32_t)base) & 0xffff))
+
+#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_SIGDDS \
+ RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_ds)
+#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
+
+/*%
+ * Whether to rate-limit updating the LRU to avoid possible thread contention.
+ * Our performance measurement has shown the cost is marginal, so it's defined
+ * to be 0 by default either with or without threads.
+ */
+#ifndef DNS_RBTDB_LIMITLRUUPDATE
+#define DNS_RBTDB_LIMITLRUUPDATE 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;
+ uint16_t attributes;
+ dns_trust_t trust;
+ struct noqname *noqname;
+ struct noqname *closest;
+ unsigned int is_mmapped : 1;
+ unsigned int next_is_relative : 1;
+ unsigned int node_is_relative : 1;
+ unsigned int resign_lsb : 1;
+ /*%<
+ * 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.
+ */
+
+ 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) link;
+
+ unsigned int heap_index;
+ /*%<
+ * Used for TTL-based cache cleaning.
+ */
+ isc_stdtime_t resign;
+ /*%<
+ * Case vector. If the bit is set then the corresponding
+ * character in the owner name needs to be AND'd with 0x20,
+ * rendering that character upper case.
+ */
+ unsigned char upper[32];
+} 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
+#define RDATASET_ATTR_NEGATIVE 0x0100
+#define RDATASET_ATTR_PREFETCH 0x0200
+#define RDATASET_ATTR_CASESET 0x0400
+#define RDATASET_ATTR_ZEROTTL 0x0800
+#define RDATASET_ATTR_CASEFULLYLOWER 0x1000
+
+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 STALE(header) \
+ (((header)->attributes & RDATASET_ATTR_STALE) != 0)
+#define RESIGN(header) \
+ (((header)->attributes & RDATASET_ATTR_RESIGN) != 0)
+#define OPTOUT(header) \
+ (((header)->attributes & RDATASET_ATTR_OPTOUT) != 0)
+#define NEGATIVE(header) \
+ (((header)->attributes & RDATASET_ATTR_NEGATIVE) != 0)
+#define PREFETCH(header) \
+ (((header)->attributes & RDATASET_ATTR_PREFETCH) != 0)
+#define CASESET(header) \
+ (((header)->attributes & RDATASET_ATTR_CASESET) != 0)
+#define ZEROTTL(header) \
+ (((header)->attributes & RDATASET_ATTR_ZEROTTL) != 0)
+#define CASEFULLYLOWER(header) \
+ (((header)->attributes & RDATASET_ATTR_CASEFULLYLOWER) != 0)
+
+
+#define ACTIVE(header, now) \
+ (((header)->rdh_ttl > (now)) || \
+ ((header)->rdh_ttl == (now) && ZEROTTL(header)))
+
+#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 than 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. */
+ bool exiting;
+} rbtdb_nodelock_t;
+
+typedef struct rbtdb_changed {
+ dns_rbtnode_t * node;
+ bool 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 dns_rbtdb dns_rbtdb_t;
+
+/* Reason for expiring a record from cache */
+typedef enum {
+ expire_lru,
+ expire_ttl,
+ expire_flush
+} expire_t;
+
+typedef struct rbtdb_version {
+ /* Not locked */
+ rbtdb_serial_t serial;
+ dns_rbtdb_t * rbtdb;
+ /*
+ * 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. */
+ bool writer;
+ bool commit_ok;
+ rbtdb_changedlist_t changed_list;
+ rdatasetheaderlist_t resigned_list;
+ ISC_LINK(struct rbtdb_version) link;
+ dns_db_secure_t secure;
+ bool havensec3;
+ /* NSEC3 parameters */
+ dns_hash_t hash;
+ uint8_t flags;
+ uint16_t iterations;
+ uint8_t salt_length;
+ unsigned char salt[DNS_NSEC3_SALTSIZE];
+
+ /*
+ * records and bytes are covered by rwlock.
+ */
+ isc_rwlock_t rwlock;
+ uint64_t records;
+ uint64_t bytes;
+} rbtdb_version_t;
+
+typedef ISC_LIST(rbtdb_version_t) rbtdb_versionlist_t;
+
+struct dns_rbtdb {
+ /* Unlocked. */
+ dns_db_t common;
+ /* Locks the data in this struct */
+#if DNS_RBTDB_USERWLOCK
+ isc_rwlock_t lock;
+#else
+ isc_mutex_t lock;
+#endif
+ /* Locks the tree structure (prevents nodes appearing/disappearing) */
+ isc_rwlock_t tree_lock;
+ /* Locks for individual tree nodes */
+ unsigned int node_lock_count;
+ rbtdb_nodelock_t * node_locks;
+ dns_rbtnode_t * origin_node;
+ dns_rbtnode_t * nsec3_origin_node;
+ dns_stats_t * rrsetstats; /* cache DB only */
+ isc_stats_t * cachestats; /* 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_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. These are used for TTL based expiry in a cache,
+ * or for zone resigning in a zone DB. hmctx is the memory
+ * context to use for the heap (which differs from the main
+ * database memory context in the case of a cache).
+ */
+ isc_mem_t *hmctx;
+ isc_heap_t **heaps;
+
+ /*
+ * Base values for the mmap() code.
+ */
+ void * mmap_location;
+ size_t mmap_size;
+
+ /* Locked by tree_lock. */
+ dns_rbt_t * tree;
+ dns_rbt_t * nsec;
+ dns_rbt_t * nsec3;
+ dns_rpz_zones_t *rpzs;
+ dns_rpz_num_t rpz_num;
+ dns_rpz_zones_t *load_rpzs;
+
+ /* Unlocked */
+ unsigned int quantum;
+};
+
+#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;
+ bool copy_name;
+ bool need_cleanup;
+ bool 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 delete_callback(void *data, void *arg);
+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 bool 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,
+ bool tree_locked, expire_t reason);
+static void overmem_purge(dns_rbtdb_t *rbtdb, unsigned int locknum_start,
+ isc_stdtime_t now, bool tree_locked);
+static isc_result_t resign_insert(dns_rbtdb_t *rbtdb, int idx,
+ rdatasetheader_t *newheader);
+static void resign_delete(dns_rbtdb_t *rbtdb, rbtdb_version_t *version,
+ rdatasetheader_t *header);
+static void prune_tree(isc_task_t *task, isc_event_t *event);
+static void rdataset_settrust(dns_rdataset_t *rdataset, dns_trust_t trust);
+static void rdataset_expire(dns_rdataset_t *rdataset);
+static void rdataset_clearprefetch(dns_rdataset_t *rdataset);
+static void rdataset_setownercase(dns_rdataset_t *rdataset,
+ const dns_name_t *name);
+static void rdataset_getownercase(const dns_rdataset_t *rdataset,
+ dns_name_t *name);
+
+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,
+ rdataset_settrust,
+ rdataset_expire,
+ rdataset_clearprefetch,
+ rdataset_setownercase,
+ rdataset_getownercase
+};
+
+static dns_rdatasetmethods_t slab_methods = {
+ rdataset_disassociate,
+ rdataset_first,
+ rdataset_next,
+ rdataset_current,
+ rdataset_clone,
+ rdataset_count,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+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;
+
+/*
+ * Note that these iterators, unless created with either DNS_DB_NSEC3ONLY or
+ * DNS_DB_NONSEC3, will transparently move between the last node of the
+ * "regular" RBT ("chain" field) and the root node of the NSEC3 RBT
+ * ("nsec3chain" field) of the database in question, as if the latter was a
+ * successor to the former in lexical order. The "current" field always holds
+ * the address of either "chain" or "nsec3chain", depending on which RBT is
+ * being traversed at given time.
+ */
+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 true, then the tree lock is not being held.
+ */
+typedef struct rbtdb_dbiterator {
+ dns_dbiterator_t common;
+ bool paused;
+ bool 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 delcnt;
+ bool nsec3only;
+ bool 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, bool log,
+ isc_event_t *event);
+static void overmem(dns_db_t *db, bool over);
+static void setnsec3parameters(dns_db_t *db, rbtdb_version_t *version);
+static void setownercase(rdatasetheader_t *header, const dns_name_t *name);
+
+static bool match_header_version(rbtdb_file_header_t *header);
+
+/* Pad to 32 bytes */
+static char FILE_VERSION[32] = "\0";
+
+/*%
+ * '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 simultaneous updates.
+ *
+ * Note:
+ * Both init_count and header->count can be 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.
+ */
+
+/*
+ * Debugging routines
+ */
+#ifdef DEBUG
+static void
+hexdump(const char *desc, unsigned char *data, size_t size) {
+ char hexdump[BUFSIZ * 2 + 1];
+ isc_buffer_t b;
+ isc_region_t r;
+ isc_result_t result;
+ size_t bytes;
+
+ fprintf(stderr, "%s: ", desc);
+ do {
+ isc_buffer_init(&b, hexdump, sizeof(hexdump));
+ r.base = data;
+ r.length = bytes = (size > BUFSIZ) ? BUFSIZ : size;
+ result = isc_hex_totext(&r, 0, "", &b);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ isc_buffer_putuint8(&b, 0);
+ fprintf(stderr, "%s", hexdump);
+ data += bytes;
+ size -= bytes;
+ } while (size > 0);
+ fprintf(stderr, "\n");
+}
+#endif
+
+
+/*
+ * 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, true, event);
+}
+
+static void
+update_cachestats(dns_rbtdb_t *rbtdb, isc_result_t result) {
+ INSIST(IS_CACHE(rbtdb));
+
+ if (rbtdb->cachestats == NULL)
+ return;
+
+ switch (result) {
+ case ISC_R_SUCCESS:
+ case DNS_R_CNAME:
+ case DNS_R_DNAME:
+ case DNS_R_DELEGATION:
+ case DNS_R_NCACHENXDOMAIN:
+ case DNS_R_NCACHENXRRSET:
+ isc_stats_increment(rbtdb->cachestats,
+ dns_cachestatscounter_hits);
+ break;
+ default:
+ isc_stats_increment(rbtdb->cachestats,
+ dns_cachestatscounter_misses);
+ }
+}
+
+static void
+update_rrsetstats(dns_rbtdb_t *rbtdb, rdatasetheader_t *header,
+ bool 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 (NEGATIVE(header)) {
+ if (NXDOMAIN(header))
+ statattributes = DNS_RDATASTATSTYPE_ATTR_NXDOMAIN;
+ else {
+ statattributes = DNS_RDATASTATSTYPE_ATTR_NXRRSET;
+ base = RBTDB_RDATATYPE_EXT(header->type);
+ }
+ } else
+ base = RBTDB_RDATATYPE_BASE(header->type);
+
+ if (STALE(header))
+ statattributes |= DNS_RDATASTATSTYPE_ATTR_STALE;
+
+ 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;
+
+
+ if (!IS_CACHE(rbtdb)) {
+ header->rdh_ttl = newttl;
+ return;
+ }
+
+ oldttl = header->rdh_ttl;
+ header->rdh_ttl = newttl;
+
+ /*
+ * 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 true if v1 happens "sooner" than v2.
+ */
+static bool
+ttl_sooner(void *v1, void *v2) {
+ rdatasetheader_t *h1 = v1;
+ rdatasetheader_t *h2 = v2;
+
+ return (h1->rdh_ttl < h2->rdh_ttl);
+}
+
+static bool
+resign_sooner(void *v1, void *v2) {
+ rdatasetheader_t *h1 = v1;
+ rdatasetheader_t *h2 = v2;
+
+ return (h1->resign < h2->resign ||
+ (h1->resign == h2->resign &&
+ h1->resign_lsb < h2->resign_lsb));
+}
+
+/*%
+ * This function sets the heap index into the header.
+ */
+static void
+set_index(void *what, unsigned int idx) {
+ rdatasetheader_t *h = what;
+
+ h->heap_index = idx;
+}
+
+/*%
+ * 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;
+ uint64_t usecs;
+ isc_time_t end;
+ unsigned int nodes;
+
+ 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);
+ }
+ nodes = old * interval;
+ nodes /= (unsigned int)usecs;
+ if (nodes == 0)
+ nodes = 1;
+ else if (nodes > 1000)
+ nodes = 1000;
+
+ /* Smooth */
+ nodes = (nodes + old * 3) / 4;
+
+ if (nodes != old)
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1),
+ "adjust_quantum: old=%d, new=%d", old, nodes);
+
+ return (nodes);
+}
+
+static void
+free_rbtdb(dns_rbtdb_t *rbtdb, bool log, isc_event_t *event) {
+ unsigned int i;
+ isc_ondestroy_t ondest;
+ isc_result_t result;
+ char buf[DNS_NAME_FORMATSIZE];
+ dns_rbt_t **treep;
+ isc_time_t start;
+ dns_dbonupdatelistener_t *listener, *listener_next;
+
+ if (IS_CACHE(rbtdb) && rbtdb->common.rdclass == dns_rdataclass_in)
+ overmem((dns_db_t *)rbtdb, (bool)-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_rwlock_destroy(&rbtdb->current_version->rwlock);
+ 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;
+
+ for (;;) {
+ /*
+ * pick the next tree to (start to) destroy
+ */
+ treep = &rbtdb->tree;
+ if (*treep == NULL) {
+ treep = &rbtdb->nsec;
+ if (*treep == NULL) {
+ treep = &rbtdb->nsec3;
+ /*
+ * we're finished after clear cutting
+ */
+ if (*treep == NULL)
+ break;
+ }
+ }
+
+ isc_time_now(&start);
+ result = dns_rbt_destroy2(treep, 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)
+ continue;
+ isc_task_send(rbtdb->task, &event);
+ return;
+ }
+ INSIST(result == ISC_R_SUCCESS && *treep == 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
+ strlcpy(buf, "<UNKNOWN>", sizeof(buf));
+ 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->hmctx, rbtdb->heaps,
+ rbtdb->node_lock_count * sizeof(isc_heap_t *));
+ }
+
+ if (rbtdb->rrsetstats != NULL)
+ dns_stats_detach(&rbtdb->rrsetstats);
+ if (rbtdb->cachestats != NULL)
+ isc_stats_detach(&rbtdb->cachestats);
+
+ if (rbtdb->load_rpzs != NULL) {
+ /*
+ * We must be cleaning up after a failed zone loading.
+ */
+ REQUIRE(rbtdb->rpzs != NULL &&
+ rbtdb->rpz_num < rbtdb->rpzs->p.num_zones);
+ dns_rpz_detach_rpzs(&rbtdb->load_rpzs);
+ }
+ if (rbtdb->rpzs != NULL) {
+ REQUIRE(rbtdb->rpz_num < rbtdb->rpzs->p.num_zones);
+ dns_rpz_detach_rpzs(&rbtdb->rpzs);
+ }
+
+ 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_detach(&rbtdb->hmctx);
+
+ if (rbtdb->mmap_location != NULL)
+ isc_file_munmap(rbtdb->mmap_location,
+ (size_t) rbtdb->mmap_size);
+
+ for (listener = ISC_LIST_HEAD(rbtdb->common.update_listeners);
+ listener != NULL;
+ listener = listener_next)
+ {
+ listener_next = ISC_LIST_NEXT(listener, link);
+ ISC_LIST_UNLINK(rbtdb->common.update_listeners, listener, link);
+ isc_mem_put(rbtdb->common.mctx, listener,
+ sizeof(dns_dbonupdatelistener_t));
+ }
+
+ isc_mem_putanddetach(&rbtdb->common.mctx, rbtdb, sizeof(*rbtdb));
+ isc_ondestroy_notify(&ondest, rbtdb);
+}
+
+static inline void
+maybe_free_rbtdb(dns_rbtdb_t *rbtdb) {
+ bool want_free = 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 = 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 = 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 {
+ strlcpy(buf, "<UNKNOWN>", sizeof(buf));
+ }
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1),
+ "calling free_rbtdb(%s)", buf);
+ free_rbtdb(rbtdb, 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, bool 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 = 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) {
+ isc_result_t result;
+ 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,
+ true);
+ if (version != NULL) {
+ version->rbtdb = rbtdb;
+ version->commit_ok = 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;
+ memmove(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));
+ }
+ result = isc_rwlock_init(&version->rwlock, 0, 0);
+ if (result != ISC_R_SUCCESS) {
+ isc_refcount_destroy(&version->references);
+ isc_mem_put(rbtdb->common.mctx, version,
+ sizeof(*version));
+ version = NULL;
+ } else {
+ RWLOCK(&rbtdb->current_version->rwlock,
+ isc_rwlocktype_read);
+ version->records = rbtdb->current_version->records;
+ version->bytes = rbtdb->current_version->bytes;
+ RWUNLOCK(&rbtdb->current_version->rwlock,
+ isc_rwlocktype_read);
+ rbtdb->next_serial++;
+ rbtdb->future_version = version;
+ }
+ } else
+ result = ISC_R_NOMEMORY;
+ RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write);
+
+ if (version == NULL)
+ return (result);
+
+ *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));
+ INSIST(rbtversion != NULL && rbtversion->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 = false;
+ ISC_LIST_INITANDAPPEND(version->changed_list, changed, link);
+ } else
+ version->commit_ok = 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, link);
+ h->heap_index = 0;
+ h->is_mmapped = 0;
+ h->next_is_relative = 0;
+ h->node_is_relative = 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
+}
+
+/*
+ * Update the copied values of 'next' and 'node' if they are relative.
+ */
+static void
+update_newheader(rdatasetheader_t *newh, rdatasetheader_t *old) {
+ char *p;
+
+ if (old->next_is_relative) {
+ p = (char *) old;
+ p += (uintptr_t)old->next;
+ newh->next = (rdatasetheader_t *)p;
+ }
+ if (old->node_is_relative) {
+ p = (char *) old;
+ p += (uintptr_t)old->node;
+ newh->node = (dns_rbtnode_t *)p;
+ }
+ if (CASESET(old)) {
+ uint16_t attr;
+
+ memmove(newh->upper, old->upper, sizeof(old->upper));
+ attr = old->attributes & (RDATASET_ATTR_CASESET |
+ RDATASET_ATTR_CASEFULLYLOWER);
+ newh->attributes |= attr;
+ }
+}
+
+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
+ memset(h->upper, 0xeb, sizeof(h->upper));
+ init_rdataset(rbtdb, h);
+ h->rdh_ttl = 0;
+ 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, false);
+ }
+
+ idx = rdataset->node->locknum;
+ if (ISC_LINK_LINKED(rdataset, link)) {
+ INSIST(IS_CACHE(rbtdb));
+ ISC_LIST_UNLINK(rbtdb->rdatasets[idx], rdataset, 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 (NONEXISTENT(rdataset))
+ size = sizeof(*rdataset);
+ else
+ size = dns_rdataslab_size((unsigned char *)rdataset,
+ sizeof(*rdataset));
+
+ if (rdataset->is_mmapped == 1)
+ return;
+
+ isc_mem_put(mctx, rdataset, size);
+}
+
+static inline void
+rollback_node(dns_rbtnode_t *node, rbtdb_serial_t serial) {
+ rdatasetheader_t *header, *dcurrent;
+ bool make_dirty = 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 = true;
+ }
+ for (dcurrent = header->down;
+ dcurrent != NULL;
+ dcurrent = dcurrent->down) {
+ if (dcurrent->serial == serial) {
+ dcurrent->attributes |= RDATASET_ATTR_IGNORE;
+ make_dirty = true;
+ }
+ }
+ }
+ if (make_dirty)
+ node->dirty = 1;
+}
+
+static inline void
+mark_stale_header(dns_rbtdb_t *rbtdb, rdatasetheader_t *header) {
+
+ /*
+ * If we are already stale there is nothing to do.
+ */
+ if ((header->attributes & RDATASET_ATTR_STALE) != 0)
+ return;
+
+ header->attributes |= RDATASET_ATTR_STALE;
+ header->node->dirty = 1;
+
+ /*
+ * If we have not been counted then there is nothing to do.
+ */
+ if ((header->attributes & RDATASET_ATTR_STATCOUNT) == 0)
+ return;
+
+ if (EXISTS(header))
+ update_rrsetstats(rbtdb, header, true);
+}
+
+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;
+ bool still_dirty = 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 = 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;
+}
+
+static void
+delete_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node) {
+ dns_rbtnode_t *nsecnode;
+ dns_fixedname_t fname;
+ dns_name_t *name;
+ isc_result_t result = ISC_R_UNEXPECTED;
+ unsigned int node_has_rpz;
+
+ INSIST(!ISC_LINK_LINKED(node, deadlink));
+
+ 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),
+ "delete_node(): %p %s (bucket %d)",
+ node,
+ dns_rbt_formatnodename(node,
+ printname, sizeof(printname)),
+ node->locknum);
+ }
+
+ switch (node->nsec) {
+ case DNS_RBT_NSEC_NORMAL:
+ /*
+ * Though this may be wasteful, it has to be done before
+ * node is deleted.
+ */
+ name = dns_fixedname_initname(&fname);
+ dns_rbt_fullnamefromnode(node, name);
+
+ /*
+ * dns_rbt_deletenode() may keep the node if it has a
+ * down pointer, but we mustn't call dns_rpz_delete() on
+ * it again.
+ */
+ node_has_rpz = node->rpz;
+ node->rpz = 0;
+ result = dns_rbt_deletenode(rbtdb->tree, node, false);
+ if (result == ISC_R_SUCCESS &&
+ rbtdb->rpzs != NULL && node_has_rpz)
+ dns_rpz_delete(rbtdb->rpzs, rbtdb->rpz_num, name);
+ break;
+ case DNS_RBT_NSEC_HAS_NSEC:
+ name = dns_fixedname_initname(&fname);
+ dns_rbt_fullnamefromnode(node, name);
+ /*
+ * Delete the corresponding node from the auxiliary NSEC
+ * tree before deleting from the main tree.
+ */
+ nsecnode = NULL;
+ result = dns_rbt_findnode(rbtdb->nsec, name, NULL, &nsecnode,
+ NULL, DNS_RBTFIND_EMPTYDATA,
+ NULL, NULL);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_CACHE, ISC_LOG_WARNING,
+ "delete_node: "
+ "dns_rbt_findnode(nsec): %s",
+ isc_result_totext(result));
+ } else {
+ result = dns_rbt_deletenode(rbtdb->nsec, nsecnode,
+ false);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(dns_lctx,
+ DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_CACHE,
+ ISC_LOG_WARNING,
+ "delete_node(): "
+ "dns_rbt_deletenode(nsecnode): %s",
+ isc_result_totext(result));
+ }
+ }
+ /*
+ * dns_rbt_deletenode() may keep the node if it has a
+ * down pointer, but we mustn't call dns_rpz_delete() on
+ * it again.
+ */
+ node_has_rpz = node->rpz;
+ node->rpz = 0;
+ result = dns_rbt_deletenode(rbtdb->tree, node, false);
+ if (result == ISC_R_SUCCESS &&
+ rbtdb->rpzs != NULL && node_has_rpz)
+ dns_rpz_delete(rbtdb->rpzs, rbtdb->rpz_num, name);
+ break;
+ case DNS_RBT_NSEC_NSEC:
+ result = dns_rbt_deletenode(rbtdb->nsec, node, false);
+ break;
+ case DNS_RBT_NSEC_NSEC3:
+ result = dns_rbt_deletenode(rbtdb->nsec3, node, false);
+ break;
+ }
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(dns_lctx,
+ DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_CACHE,
+ ISC_LOG_WARNING,
+ "delete_node(): "
+ "dns_rbt_deletenode: %s",
+ isc_result_totext(result));
+ }
+}
+
+/*
+ * Caller must be holding the node lock.
+ */
+static inline void
+new_reference(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node) {
+ unsigned int lockrefs, noderefs;
+ isc_refcount_t *lockref;
+
+ INSIST(!ISC_LINK_LINKED(node, deadlink));
+ 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);
+}
+
+/*%
+ * 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;
+ 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);
+
+ if (node->parent != NULL &&
+ node->parent->down == node && node->left == NULL &&
+ node->right == NULL && rbtdb->task != NULL)
+ {
+ isc_event_t *ev;
+ dns_db_t *db;
+
+ ev = isc_event_allocate(rbtdb->common.mctx, NULL,
+ DNS_EVENT_RBTPRUNE,
+ prune_tree, node,
+ sizeof(isc_event_t));
+ if (ev != NULL) {
+ new_reference(rbtdb, node);
+ db = NULL;
+ attach((dns_db_t *)rbtdb, &db);
+ ev->ev_sender = db;
+ isc_task_send(rbtdb->task, &ev);
+ } else {
+ ISC_LIST_APPEND(rbtdb->deadnodes[bucketnum],
+ node, deadlink);
+ }
+ } else {
+ delete_node(rbtdb, node);
+ }
+ node = ISC_LIST_HEAD(rbtdb->deadnodes[bucketnum]);
+ count--;
+ }
+}
+
+/*
+ * 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 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_rwlocktype_t locktype = isc_rwlocktype_read;
+ nodelock_t *nodelock = &rbtdb->node_locks[node->locknum].lock;
+ bool maybe_cleanup = false;
+
+ POST(locktype);
+
+ NODE_STRONGLOCK(nodelock);
+ NODE_WEAKLOCK(nodelock, locktype);
+
+ /*
+ * Check if we can possibly cleanup the dead node. If so, upgrade
+ * the node lock below to perform the cleanup.
+ */
+ if (!ISC_LIST_EMPTY(rbtdb->deadnodes[node->locknum]) &&
+ treelocktype == isc_rwlocktype_write) {
+ maybe_cleanup = true;
+ }
+
+ if (ISC_LINK_LINKED(node, deadlink) || maybe_cleanup) {
+ /*
+ * Upgrade the lock and test if we still need to unlink.
+ */
+ NODE_WEAKUNLOCK(nodelock, locktype);
+ locktype = isc_rwlocktype_write;
+ POST(locktype);
+ NODE_WEAKLOCK(nodelock, locktype);
+ if (ISC_LINK_LINKED(node, deadlink))
+ ISC_LIST_UNLINK(rbtdb->deadnodes[node->locknum],
+ node, deadlink);
+ if (maybe_cleanup)
+ cleanup_dead_nodes(rbtdb, node->locknum);
+ }
+
+ new_reference(rbtdb, node);
+
+ NODE_WEAKUNLOCK(nodelock, locktype);
+ NODE_STRONGUNLOCK(nodelock);
+}
+
+/*
+ * 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 true if and only if the node reference decreases
+ * to zero.
+ *
+ * NOTE: Decrementing the reference count of a node to zero does not mean it
+ * will be immediately freed.
+ */
+static bool
+decrement_reference(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
+ rbtdb_serial_t least_serial,
+ isc_rwlocktype_t nlock, isc_rwlocktype_t tlock,
+ bool pruning)
+{
+ isc_result_t result;
+ bool write_locked;
+ rbtdb_nodelock_t *nodelock;
+ unsigned int refs, nrefs;
+ int bucket = node->locknum;
+ bool no_reference = true;
+
+ nodelock = &rbtdb->node_locks[bucket];
+
+#define KEEP_NODE(n, r) \
+ ((n)->data != NULL || (n)->down != NULL || \
+ (n) == (r)->origin_node || (n) == (r)->nsec3_origin_node)
+
+ /* Handle easy and typical case first. */
+ if (!node->dirty && KEEP_NODE(node, rbtdb)) {
+ 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) ? true : 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 (false);
+ }
+
+ if (node->dirty) {
+ 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);
+ }
+ }
+
+ /*
+ * 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 = (result == ISC_R_SUCCESS);
+ } else
+ write_locked = true;
+
+ isc_refcount_decrement(&nodelock->references, &refs);
+ INSIST((int)refs >= 0);
+
+ if (KEEP_NODE(node, rbtdb))
+ goto restore_locks;
+
+#undef KEEP_NODE
+
+ if (write_locked) {
+ /*
+ * We can now delete the node.
+ */
+
+ /*
+ * If this node is the only one in the level it's in, deleting
+ * this node may recursively make its parent the only node in
+ * the parent level; if so, and if no one is currently using
+ * the parent node, this is almost the only opportunity to
+ * clean it up. But the recursive cleanup is not that trivial
+ * since the child and parent may be in different lock buckets,
+ * which would cause a lock order reversal problem. To avoid
+ * the trouble, we'll dispatch a separate event for batch
+ * cleaning. We need to check whether we're deleting the node
+ * as a result of pruning to avoid infinite dispatching.
+ * Note: pruning happens only when a task has been set for the
+ * rbtdb. If the user of the rbtdb chooses not to set a task,
+ * it's their responsibility to purge stale leaves (e.g. by
+ * periodic walk-through).
+ */
+ if (!pruning && node->parent != NULL &&
+ node->parent->down == node && node->left == NULL &&
+ node->right == NULL && rbtdb->task != NULL) {
+ isc_event_t *ev;
+ dns_db_t *db;
+
+ ev = isc_event_allocate(rbtdb->common.mctx, NULL,
+ DNS_EVENT_RBTPRUNE,
+ prune_tree, node,
+ sizeof(isc_event_t));
+ if (ev != NULL) {
+ new_reference(rbtdb, node);
+ db = NULL;
+ attach((dns_db_t *)rbtdb, &db);
+ ev->ev_sender = db;
+ isc_task_send(rbtdb->task, &ev);
+ no_reference = false;
+ } else {
+ /*
+ * XXX: this is a weird situation. We could
+ * ignore this error case, but then the stale
+ * node will unlikely be purged except via a
+ * rare condition such as manual cleanup. So
+ * we queue it in the deadnodes list, hoping
+ * the memory shortage is temporary and the node
+ * will be deleted later.
+ */
+ isc_log_write(dns_lctx,
+ DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_CACHE,
+ ISC_LOG_INFO,
+ "decrement_reference: failed to "
+ "allocate pruning event");
+ INSIST(node->data == NULL);
+ INSIST(!ISC_LINK_LINKED(node, deadlink));
+ ISC_LIST_APPEND(rbtdb->deadnodes[bucket], node,
+ deadlink);
+ }
+ } else {
+ delete_node(rbtdb, node);
+ }
+ } else {
+ INSIST(node->data == NULL);
+ INSIST(!ISC_LINK_LINKED(node, deadlink));
+ ISC_LIST_APPEND(rbtdb->deadnodes[bucket], node, deadlink);
+ }
+
+ restore_locks:
+ /* 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 (no_reference);
+}
+
+/*
+ * Prune the tree by recursively cleaning-up single leaves. In the worst
+ * case, the number of iteration is the number of tree levels, which is at
+ * most the maximum number of domain name labels, i.e, 127. In practice, this
+ * should be much smaller (only a few times), and even the worst case would be
+ * acceptable for a single event.
+ */
+static void
+prune_tree(isc_task_t *task, isc_event_t *event) {
+ dns_rbtdb_t *rbtdb = event->ev_sender;
+ dns_rbtnode_t *node = event->ev_arg;
+ dns_rbtnode_t *parent;
+ unsigned int locknum;
+
+ UNUSED(task);
+
+ isc_event_free(&event);
+
+ RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
+ locknum = node->locknum;
+ NODE_LOCK(&rbtdb->node_locks[locknum].lock, isc_rwlocktype_write);
+ do {
+ parent = node->parent;
+ decrement_reference(rbtdb, node, 0, isc_rwlocktype_write,
+ isc_rwlocktype_write, true);
+
+ if (parent != NULL && parent->down == NULL) {
+ /*
+ * node was the only down child of the parent and has
+ * just been removed. We'll then need to examine the
+ * parent. Keep the lock if possible; otherwise,
+ * release the old lock and acquire one for the parent.
+ */
+ if (parent->locknum != locknum) {
+ NODE_UNLOCK(&rbtdb->node_locks[locknum].lock,
+ isc_rwlocktype_write);
+ locknum = parent->locknum;
+ NODE_LOCK(&rbtdb->node_locks[locknum].lock,
+ isc_rwlocktype_write);
+ }
+
+ /*
+ * We need to gain a reference to the node before
+ * decrementing it in the next iteration. In addition,
+ * if the node is in the dead-nodes list, extract it
+ * from the list beforehand as we do in
+ * reactivate_node().
+ */
+ if (ISC_LINK_LINKED(parent, deadlink))
+ ISC_LIST_UNLINK(rbtdb->deadnodes[locknum],
+ parent, deadlink);
+ new_reference(rbtdb, parent);
+ } else
+ parent = NULL;
+
+ node = parent;
+ } while (node != NULL);
+ NODE_UNLOCK(&rbtdb->node_locks[locknum].lock, isc_rwlocktype_write);
+ RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
+
+ detach((dns_db_t **)&rbtdb);
+}
+
+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;
+ bool haszonekey = false;
+ bool hasnsec = 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) {
+ result = dns_rdataset_first(&keyset);
+ while (result == ISC_R_SUCCESS) {
+ dns_rdata_t keyrdata = DNS_RDATA_INIT;
+ dns_rdataset_current(&keyset, &keyrdata);
+ if (dns_zonekey_iszonekey(&keyrdata)) {
+ haszonekey = true;
+ break;
+ }
+ result = dns_rdataset_next(&keyset);
+ }
+ dns_rdataset_disassociate(&keyset);
+ }
+ if (!haszonekey) {
+ version->secure = dns_db_insecure;
+ version->havensec3 = 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 = true;
+ dns_rdataset_disassociate(&signsecset);
+ }
+ dns_rdataset_disassociate(&nsecset);
+ }
+
+ setnsec3parameters(db, version);
+
+ /*
+ * Do we have a valid NSEC/NSEC3 chain?
+ */
+ if (version->havensec3 || hasnsec)
+ version->secure = dns_db_secure;
+ 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) {
+ 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 = 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;
+
+ if (nsec3param.flags != 0)
+ continue;
+
+ memmove(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 = 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
+cleanup_dead_nodes_callback(isc_task_t *task, isc_event_t *event) {
+ dns_rbtdb_t *rbtdb = event->ev_arg;
+ bool again = false;
+ unsigned int locknum;
+ unsigned int refs;
+
+ RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
+ for (locknum = 0; locknum < rbtdb->node_lock_count; locknum++) {
+ NODE_LOCK(&rbtdb->node_locks[locknum].lock,
+ isc_rwlocktype_write);
+ cleanup_dead_nodes(rbtdb, locknum);
+ if (ISC_LIST_HEAD(rbtdb->deadnodes[locknum]) != NULL)
+ again = true;
+ NODE_UNLOCK(&rbtdb->node_locks[locknum].lock,
+ isc_rwlocktype_write);
+ }
+ RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
+ if (again)
+ isc_task_send(task, &event);
+ else {
+ isc_event_free(&event);
+ isc_refcount_decrement(&rbtdb->references, &refs);
+ if (refs == 0)
+ maybe_free_rbtdb(rbtdb);
+ }
+}
+
+static void
+closeversion(dns_db_t *db, dns_dbversion_t **versionp, bool commit) {
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+ rbtdb_version_t *version, *cleanup_version, *least_greater;
+ bool rollback = 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;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+ version = (rbtdb_version_t *)*versionp;
+ INSIST(version->rbtdb == rbtdb);
+
+ 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;
+ }
+
+ /*
+ * Update the zone's secure status in version before making
+ * it the current version.
+ */
+ if (version->writer && commit && !IS_CACHE(rbtdb))
+ iszonesecure(db, version, rbtdb->origin_node);
+
+ RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write);
+ serial = version->serial;
+ 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 = 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 = 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);
+
+ if (cleanup_version != NULL) {
+ INSIST(EMPTY(cleanup_version->changed_list));
+ isc_rwlock_destroy(&cleanup_version->rwlock);
+ 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)) {
+ nodelock_t *lock;
+
+ ISC_LIST_UNLINK(resigned_list, header, link);
+
+ lock = &rbtdb->node_locks[header->node->locknum].lock;
+ NODE_LOCK(lock, isc_rwlocktype_write);
+ if (rollback && !IGNORE(header)) {
+ isc_result_t result;
+ result = resign_insert(rbtdb, header->node->locknum,
+ header);
+ if (result != ISC_R_SUCCESS)
+ isc_log_write(dns_lctx,
+ DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_ZONE, ISC_LOG_ERROR,
+ "Unable to reinsert header to "
+ "re-signing heap: %s\n",
+ dns_result_totext(result));
+ }
+ decrement_reference(rbtdb, header->node, least_serial,
+ isc_rwlocktype_write, isc_rwlocktype_none,
+ false);
+ NODE_UNLOCK(lock, isc_rwlocktype_write);
+ }
+
+ if (!EMPTY(cleanup_list)) {
+ isc_event_t *event = NULL;
+ isc_rwlocktype_t tlock = isc_rwlocktype_none;
+
+ if (rbtdb->task != NULL)
+ event = isc_event_allocate(rbtdb->common.mctx, NULL,
+ DNS_EVENT_RBTDEADNODES,
+ cleanup_dead_nodes_callback,
+ rbtdb, sizeof(isc_event_t));
+ if (event == NULL) {
+ /*
+ * 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);
+ tlock = 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.
+ */
+ if (event == NULL)
+ cleanup_dead_nodes(rbtdb, rbtnode->locknum);
+
+ if (rollback)
+ rollback_node(rbtnode, serial);
+ decrement_reference(rbtdb, rbtnode, least_serial,
+ isc_rwlocktype_write, tlock,
+ false);
+
+ NODE_UNLOCK(lock, isc_rwlocktype_write);
+
+ isc_mem_put(rbtdb->common.mctx, changed,
+ sizeof(*changed));
+ }
+ if (event != NULL) {
+ isc_refcount_increment(&rbtdb->references, NULL);
+ isc_task_send(rbtdb->task, &event);
+ } else
+ 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);
+ if (result == ISC_R_SUCCESS)
+ node->nsec = DNS_RBT_NSEC_NORMAL;
+ 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);
+ if (result == ISC_R_SUCCESS)
+ node->nsec = DNS_RBT_NSEC_NORMAL;
+ }
+ i++;
+ }
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+findnodeintree(dns_rbtdb_t *rbtdb, dns_rbt_t *tree, dns_name_t *name,
+ bool create, dns_dbnode_t **nodep)
+{
+ dns_rbtnode_t *node = NULL;
+ dns_name_t nodename;
+ isc_result_t result;
+ isc_rwlocktype_t locktype = isc_rwlocktype_read;
+
+ INSIST(tree == rbtdb->tree || tree == rbtdb->nsec3);
+
+ dns_name_init(&nodename, NULL);
+ RWLOCK(&rbtdb->tree_lock, locktype);
+ result = dns_rbt_findnode(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(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, true) %
+ rbtdb->node_lock_count;
+#endif
+ if (tree == rbtdb->tree) {
+ 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);
+ }
+ }
+ }
+ if (tree == rbtdb->nsec3)
+ node->nsec = DNS_RBT_NSEC_NSEC3;
+ } else if (result != ISC_R_EXISTS) {
+ RWUNLOCK(&rbtdb->tree_lock, locktype);
+ return (result);
+ }
+ }
+
+ if (tree == rbtdb->nsec3)
+ INSIST(node->nsec == DNS_RBT_NSEC_NSEC3);
+
+ reactivate_node(rbtdb, node, locktype);
+
+ /*
+ * Always try to add the policy zone data, because this node might
+ * already have been implicitly created by the previous addition of
+ * a longer domain. A common example is adding *.example.com
+ * (implicitly creating example.com) followed by explicitly adding
+ * example.com.
+ */
+ if (create && rbtdb->rpzs != NULL && tree == rbtdb->tree) {
+ dns_fixedname_t fnamef;
+ dns_name_t *fname;
+
+ dns_fixedname_init(&fnamef);
+ fname = dns_fixedname_name(&fnamef);
+ dns_rbt_fullnamefromnode(node, fname);
+ result = dns_rpz_add(rbtdb->rpzs, rbtdb->rpz_num, fname);
+ if (result == ISC_R_SUCCESS)
+ node->rpz = 1;
+ if (result != ISC_R_SUCCESS && result != ISC_R_EXISTS) {
+ /*
+ * It is too late to give up, so merely complain.
+ */
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+ DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL,
+ "dns_rpz_add(): %s",
+ isc_result_totext(result));
+ }
+ }
+
+ RWUNLOCK(&rbtdb->tree_lock, locktype);
+
+ *nodep = (dns_dbnode_t *)node;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+findnode(dns_db_t *db, dns_name_t *name, bool create,
+ dns_dbnode_t **nodep)
+{
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+
+ return (findnodeintree(rbtdb, rbtdb->tree, name, create, nodep));
+}
+
+static isc_result_t
+findnsec3node(dns_db_t *db, dns_name_t *name, bool create,
+ dns_dbnode_t **nodep)
+{
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+
+ return (findnodeintree(rbtdb, rbtdb->nsec3, name, create, nodep));
+}
+
+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 (!IS_CACHE(search->rbtdb) && !IS_STUB(search->rbtdb) &&
+ ns_header != NULL) {
+ /*
+ * Note that NS has precedence over DNAME if both exist
+ * in a zone. Otherwise DNAME take precedence over NS.
+ */
+ found = ns_header;
+ search->zonecut_sigrdataset = NULL;
+ } else if (dname_header != NULL) {
+ 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 = 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 = 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 = 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 = 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 (NEGATIVE(header))
+ rdataset->attributes |= DNS_RDATASETATTR_NEGATIVE;
+ if (NXDOMAIN(header))
+ rdataset->attributes |= DNS_RDATASETATTR_NXDOMAIN;
+ if (OPTOUT(header))
+ rdataset->attributes |= DNS_RDATASETATTR_OPTOUT;
+ if (PREFETCH(header))
+ rdataset->attributes |= DNS_RDATASETATTR_PREFETCH;
+ rdataset->private1 = rbtdb;
+ rdataset->private2 = node;
+ raw = (unsigned char *)header + sizeof(*header);
+ rdataset->private3 = raw;
+ rdataset->count = header->count++;
+ if (rdataset->count == 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 << 1) | header->resign_lsb;
+ } 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 = 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 bool
+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;
+ bool valid = 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 (false);
+ }
+ } else if (type != dns_rdatatype_a &&
+ type != dns_rdatatype_aaaa &&
+ type != dns_rdatatype_a6) {
+ return (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 = true;
+ break;
+ }
+ }
+
+ return (valid);
+}
+
+static inline bool
+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;
+ bool answer = false;
+ rdatasetheader_t *header;
+
+ rbtdb = search->rbtdb;
+
+ dns_name_init(&prefix, NULL);
+ next = dns_fixedname_initname(&fnext);
+ origin = dns_fixedname_initname(&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 = true;
+ return (answer);
+}
+
+static inline bool
+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;
+ bool check_next = true;
+ bool check_prev = true;
+ bool answer = 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);
+ next = dns_fixedname_initname(&fnext);
+ prev = dns_fixedname_initname(&fprev);
+ origin = dns_fixedname_initname(&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 = 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 = 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 = 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;
+ bool 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 = 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 = true;
+ else
+ active = false;
+
+ if (node->wild)
+ wild = true;
+ else
+ wild = 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);
+ wname = dns_fixedname_initname(&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 = true;
+ } while (!done);
+
+ return (result);
+}
+
+static bool
+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 (true);
+ dns_rdata_reset(&rdata);
+ }
+ return (false);
+}
+
+/*
+ * Find node of the NSEC/NSEC3 record that is 'name'.
+ */
+static inline isc_result_t
+previous_closest_nsec(dns_rdatatype_t type, rbtdb_search_t *search,
+ dns_name_t *name, dns_name_t *origin,
+ dns_rbtnode_t **nodep, dns_rbtnodechain_t *nsecchain,
+ bool *firstp)
+{
+ dns_fixedname_t ftarget;
+ dns_name_t *target;
+ dns_rbtnode_t *nsecnode;
+ isc_result_t result;
+
+ REQUIRE(nodep != NULL && *nodep == NULL);
+
+ if (type == dns_rdatatype_nsec3) {
+ result = dns_rbtnodechain_prev(&search->chain, NULL, NULL);
+ if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN)
+ return (result);
+ result = dns_rbtnodechain_current(&search->chain, name, origin,
+ nodep);
+ return (result);
+ }
+
+ target = dns_fixedname_initname(&ftarget);
+
+ for (;;) {
+ if (*firstp) {
+ /*
+ * Construct the name of the second node to check.
+ * It is the first node sought in the NSEC tree.
+ */
+ *firstp = false;
+ dns_rbtnodechain_init(nsecchain, NULL);
+ result = dns_name_concatenate(name, origin,
+ target, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ nsecnode = NULL;
+ result = dns_rbt_findnode(search->rbtdb->nsec,
+ target, NULL,
+ &nsecnode, nsecchain,
+ DNS_RBTFIND_NOOPTIONS,
+ NULL, NULL);
+ if (result == ISC_R_SUCCESS) {
+ /*
+ * Since this was the first loop, finding the
+ * name in the NSEC tree implies that the first
+ * node checked in the main tree had an
+ * unacceptable NSEC record.
+ * Try the previous node in the NSEC tree.
+ */
+ result = dns_rbtnodechain_prev(nsecchain,
+ name, origin);
+ if (result == DNS_R_NEWORIGIN)
+ result = ISC_R_SUCCESS;
+ } else if (result == ISC_R_NOTFOUND ||
+ result == DNS_R_PARTIALMATCH) {
+ result = dns_rbtnodechain_current(nsecchain,
+ name, origin, NULL);
+ if (result == ISC_R_NOTFOUND)
+ result = ISC_R_NOMORE;
+ }
+ } else {
+ /*
+ * This is a second or later trip through the auxiliary
+ * tree for the name of a third or earlier NSEC node in
+ * the main tree. Previous trips through the NSEC tree
+ * must have found nodes in the main tree with NSEC
+ * records. Perhaps they lacked signature records.
+ */
+ result = dns_rbtnodechain_prev(nsecchain, name, origin);
+ if (result == DNS_R_NEWORIGIN)
+ result = ISC_R_SUCCESS;
+ }
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ /*
+ * Construct the name to seek in the main tree.
+ */
+ result = dns_name_concatenate(name, origin, target, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ *nodep = NULL;
+ result = dns_rbt_findnode(search->rbtdb->tree, target, NULL,
+ nodep, &search->chain,
+ DNS_RBTFIND_NOOPTIONS, NULL, NULL);
+ if (result == ISC_R_SUCCESS)
+ return (result);
+
+ /*
+ * There should always be a node in the main tree with the
+ * same name as the node in the auxiliary NSEC tree, except for
+ * nodes in the auxiliary tree that are awaiting deletion.
+ */
+ if (result != DNS_R_PARTIALMATCH && result != ISC_R_NOTFOUND) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_CACHE, ISC_LOG_ERROR,
+ "previous_closest_nsec(): %s",
+ isc_result_totext(result));
+ return (DNS_R_BADDB);
+ }
+ }
+}
+
+/*
+ * Find the NSEC/NSEC3 which is or before the current point on the
+ * search chain. For NSEC3 records only NSEC3 records that match the
+ * current NSEC3PARAM record are considered.
+ */
+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, *prevnode;
+ rdatasetheader_t *header, *header_next, *found, *foundsig;
+ dns_rbtnodechain_t nsecchain;
+ bool empty_node;
+ isc_result_t result;
+ dns_fixedname_t fname, forigin;
+ dns_name_t *name, *origin;
+ dns_rdatatype_t type;
+ rbtdb_rdatatype_t sigtype;
+ bool wraps;
+ bool first = true;
+ bool need_sig = (secure == dns_db_secure);
+
+ if (tree == search->rbtdb->nsec3) {
+ type = dns_rdatatype_nsec3;
+ sigtype = RBTDB_RDATATYPE_SIGNSEC3;
+ wraps = true;
+ } else {
+ type = dns_rdatatype_nsec;
+ sigtype = RBTDB_RDATATYPE_SIGNSEC;
+ wraps = false;
+ }
+
+ /*
+ * Use the auxiliary tree only starting with the second node in the
+ * hope that the original node will be right much of the time.
+ */
+ name = dns_fixedname_initname(&fname);
+ origin = dns_fixedname_initname(&forigin);
+ again:
+ node = NULL;
+ prevnode = NULL;
+ result = dns_rbtnodechain_current(&search->chain, name, origin, &node);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ do {
+ NODE_LOCK(&(search->rbtdb->node_locks[node->locknum].lock),
+ isc_rwlocktype_read);
+ found = NULL;
+ foundsig = NULL;
+ empty_node = 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 = 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 = true;
+ found = NULL;
+ foundsig = NULL;
+ result = previous_closest_nsec(type, search,
+ name, origin,
+ &prevnode, 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 = true;
+ result = previous_closest_nsec(type, search,
+ name, origin,
+ &prevnode,
+ &nsecchain,
+ &first);
+ } 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 = previous_closest_nsec(type, search,
+ name, origin, &prevnode,
+ &nsecchain, &first);
+ }
+ NODE_UNLOCK(&(search->rbtdb->node_locks[node->locknum].lock),
+ isc_rwlocktype_read);
+ node = prevnode;
+ prevnode = NULL;
+ } while (empty_node && result == ISC_R_SUCCESS);
+
+ if (!first)
+ dns_rbtnodechain_invalidate(&nsecchain);
+
+ 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 = 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;
+ bool cname_ok = true;
+ bool close_version = false;
+ bool maybe_zonecut = false;
+ bool at_zonecut = false;
+ bool wild;
+ bool empty_node;
+ rdatasetheader_t *header, *header_next, *found, *nsecheader;
+ rdatasetheader_t *foundsig, *cnamesig, *nsecsig;
+ rbtdb_rdatatype_t sigtype;
+ bool active;
+ dns_rbtnodechain_t chain;
+ nodelock_t *lock;
+ dns_rbt_t *tree;
+
+ search.rbtdb = (dns_rbtdb_t *)db;
+
+ REQUIRE(VALID_RBTDB(search.rbtdb));
+ INSIST(version == NULL ||
+ ((rbtdb_version_t *)version)->rbtdb == (dns_rbtdb_t *)db);
+
+ /*
+ * 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 = true;
+ }
+
+ search.rbtversion = version;
+ search.serial = search.rbtversion->serial;
+ search.options = options;
+ search.copy_name = false;
+ search.need_cleanup = false;
+ search.wild = 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 = 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 = 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 = false;
+ } else {
+ /*
+ * The node may be a zone cut itself. If it might be one,
+ * make sure we check for it later.
+ *
+ * DS records live above the zone cut in ordinary zone so
+ * we want to ignore any referral.
+ *
+ * Stub zones don't have anything "above" the delgation so
+ * we always return a referral.
+ */
+ if (node->find_callback &&
+ ((node != search.rbtdb->origin_node &&
+ !dns_rdatatype_atparent(type)) ||
+ IS_STUB(search.rbtdb)))
+ maybe_zonecut = 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 = false;
+
+ /*
+ * We now go looking for rdata...
+ */
+
+ lock = &search.rbtdb->node_locks[node->locknum].lock;
+ NODE_LOCK(lock, isc_rwlocktype_read);
+
+ found = NULL;
+ foundsig = NULL;
+ sigtype = RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, type);
+ nsecheader = NULL;
+ nsecsig = NULL;
+ cnamesig = NULL;
+ empty_node = 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 = 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 = true;
+ maybe_zonecut = false;
+ at_zonecut = 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)) {
+ NODE_UNLOCK(lock, isc_rwlocktype_read);
+ 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) {
+ 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.
+ */
+ 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;
+ }
+
+ 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)) {
+ 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 = 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(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;
+ INSIST(node != NULL);
+ 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,
+ false);
+ NODE_UNLOCK(lock, isc_rwlocktype_read);
+ }
+
+ if (close_version)
+ closeversion(db, &version, 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!");
+
+ /* NOTREACHED */
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static bool
+check_stale_header(dns_rbtnode_t *node, rdatasetheader_t *header,
+ isc_rwlocktype_t *locktype, nodelock_t *lock,
+ rbtdb_search_t *search, rdatasetheader_t **header_prev)
+{
+
+#if !defined(ISC_RWLOCK_USEATOMIC) || !defined(DNS_RBT_USEISCREFCOUNT)
+ UNUSED(lock);
+#endif
+
+ if (!ACTIVE(header, 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 {
+ mark_stale_header(search->rbtdb, header);
+ *header_prev = header;
+ }
+ } else
+ *header_prev = header;
+ return (true);
+ }
+ return (false);
+}
+
+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 (check_stale_header(node, header,
+ &locktype, lock, search,
+ &header_prev)) {
+ /* Do nothing. */
+ } 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 &&
+ (!DNS_TRUST_PENDING(dname_header->trust) ||
+ (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 = 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;
+ bool done;
+ nodelock_t *lock;
+ isc_rwlocktype_t locktype;
+
+ /*
+ * Caller must be holding the tree lock.
+ */
+
+ rbtdb = search->rbtdb;
+ i = search->chain.level_matches;
+ done = 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 (check_stale_header(node, header,
+ &locktype, lock, search,
+ &header_prev)) {
+ /* Do nothing. */
+ } 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;
+ POST(locktype);
+ }
+ 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 = 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;
+ bool 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;
+ name = dns_fixedname_initname(&fname);
+ origin = dns_fixedname_initname(&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 = true;
+ header_prev = NULL;
+ for (header = node->data; header != NULL; header = header_next) {
+ header_next = header->next;
+ if (check_stale_header(node, header,
+ &locktype, lock, search,
+ &header_prev)) {
+ continue;
+ }
+ if (NONEXISTENT(header) ||
+ RBTDB_RDATATYPE_BASE(header->type) == 0) {
+ header_prev = header;
+ continue;
+ }
+ empty_node = 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);
+}
+
+/*
+ * Connect this RBTDB to the response policy zone summary data for the view.
+ */
+static void
+rpz_attach(dns_db_t *db, dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num) {
+ dns_rbtdb_t * rbtdb;
+
+ rbtdb = (dns_rbtdb_t *)db;
+ REQUIRE(VALID_RBTDB(rbtdb));
+
+ RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
+ REQUIRE(rbtdb->rpzs == NULL && rbtdb->rpz_num == DNS_RPZ_INVALID_NUM);
+ dns_rpz_attach_rpzs(rpzs, &rbtdb->rpzs);
+ rbtdb->rpz_num = rpz_num;
+ RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
+}
+
+/*
+ * Enable this RBTDB as a response policy zone.
+ */
+static isc_result_t
+rpz_ready(dns_db_t *db) {
+ dns_rbtdb_t * rbtdb;
+ isc_result_t result;
+
+ rbtdb = (dns_rbtdb_t *)db;
+ REQUIRE(VALID_RBTDB(rbtdb));
+
+ RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
+ if (rbtdb->rpzs == NULL) {
+ INSIST(rbtdb->rpz_num == DNS_RPZ_INVALID_NUM);
+ result = ISC_R_SUCCESS;
+ } else {
+ result = dns_rpz_ready(rbtdb->rpzs, &rbtdb->load_rpzs,
+ rbtdb->rpz_num);
+ }
+ RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
+ 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;
+ bool cname_ok = true;
+ bool 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 = false;
+ search.need_cleanup = false;
+ search.wild = 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 = 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 = true;
+ header_prev = NULL;
+ for (header = node->data; header != NULL; header = header_next) {
+ header_next = header->next;
+ if (check_stale_header(node, header,
+ &locktype, lock, &search,
+ &header_prev)) {
+ /* Do nothing. */
+ } else if (EXISTS(header) && (!STALE(header))) {
+ /*
+ * We now know that there is at least one active
+ * non-stale rdataset at this node.
+ */
+ empty_node = 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.
+ */
+ 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 ||
+ (DNS_TRUST_ADDITIONAL(found->trust) &&
+ ((options & DNS_DBFIND_ADDITIONALOK) == 0)) ||
+ (found->trust == dns_trust_glue &&
+ ((options & DNS_DBFIND_GLUEOK) == 0)) ||
+ (DNS_TRUST_PENDING(found->trust) &&
+ ((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 (NEGATIVE(found)) {
+ /*
+ * 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 (!NEGATIVE(found) && 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;
+ POST(locktype);
+ }
+ 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;
+ INSIST(node != NULL);
+ 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,
+ false);
+ NODE_UNLOCK(lock, isc_rwlocktype_read);
+ }
+
+ dns_rbtnodechain_reset(&search.chain);
+
+ update_cachestats(search.rbtdb, result);
+ 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 = false;
+ search.need_cleanup = false;
+ search.wild = 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 (check_stale_header(node, header,
+ &locktype, lock, &search,
+ &header_prev)) {
+ /* Do nothing. */
+ } 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;
+ POST(locktype);
+ }
+ 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;
+ bool want_free = false;
+ bool inactive = 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, false)) {
+ if (isc_refcount_current(&nodelock->references) == 0 &&
+ nodelock->exiting) {
+ inactive = 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 = 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
+ strlcpy(buf, "<UNKNOWN>", sizeof(buf));
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1),
+ "calling free_rbtdb(%s)", buf);
+ free_rbtdb(rbtdb, 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;
+ bool force_expire = false;
+ /*
+ * These are the category and module used by the cache cleaner.
+ */
+ bool log = 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 (isc_mem_isovermem(rbtdb->common.mctx)) {
+ uint32_t val;
+
+ isc_random_get(&val);
+ /*
+ * XXXDCL Could stand to have a better policy, like LRU.
+ */
+ force_expire = (rbtnode->down == NULL && val % 4 == 0);
+
+ /*
+ * Note that 'log' can be true IFF overmem is also true.
+ * overmem can currently only be true for cache
+ * databases -- hence all of the "overmem cache" log strings.
+ */
+ log = 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.
+ */
+ mark_stale_header(rbtdb, header);
+ 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);
+ mark_stale_header(rbtdb, header);
+ } else if (log) {
+ isc_log_write(dns_lctx, category, module,
+ level, "overmem cache: "
+ "reprieve by RETAIN() %s",
+ printname);
+ }
+ } else if (isc_mem_isovermem(rbtdb->common.mctx) && 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, bool over) {
+ /* This is an empty callback. See adb.c:water() */
+
+ UNUSED(db);
+ UNUSED(over);
+
+ return;
+}
+
+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;
+ bool 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 = true;
+ fprintf(out, "\ttype %u", current->type);
+ do {
+ if (!first)
+ fprintf(out, "\t");
+ first = 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 << 1) |
+ current->resign_lsb);
+ 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 = (options & DNS_DB_RELATIVENAMES);
+ rbtdbiter->common.magic = DNS_DBITERATOR_MAGIC;
+ rbtdbiter->common.cleaning = false;
+ rbtdbiter->paused = 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->delcnt = 0;
+ rbtdbiter->nsec3only = (options & DNS_DB_NSEC3ONLY);
+ rbtdbiter->nonsec3 = (options & DNS_DB_NONSEC3);
+ 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;
+ bool close_version = false;
+ rbtdb_rdatatype_t matchtype, sigmatchtype;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+ REQUIRE(type != dns_rdatatype_any);
+ INSIST(rbtversion == NULL || rbtversion->rbtdb == rbtdb);
+
+ if (rbtversion == NULL) {
+ currentversion(db, (dns_dbversion_t **) (void *)(&rbtversion));
+ close_version = 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),
+ 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 (!ACTIVE(header, 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.
+ */
+ mark_stale_header(rbtdb, header);
+ }
+ } else if (EXISTS(header) && (!STALE(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 (!NEGATIVE(found) && foundsig != NULL)
+ bind_rdataset(rbtdb, rbtnode, foundsig, now,
+ sigrdataset);
+ }
+
+ NODE_UNLOCK(lock, locktype);
+
+ if (found == NULL)
+ return (ISC_R_NOTFOUND);
+
+ if (NEGATIVE(found)) {
+ /*
+ * We found a negative cache entry.
+ */
+ if (NXDOMAIN(found))
+ result = DNS_R_NCACHENXDOMAIN;
+ else
+ result = DNS_R_NCACHENXRRSET;
+ }
+
+ update_cachestats(rbtdb, result);
+
+ 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 {
+ INSIST(rbtversion->rbtdb == rbtdb);
+
+ 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 bool
+cname_and_other_data(dns_rbtnode_t *node, rbtdb_serial_t serial) {
+ rdatasetheader_t *header, *header_next;
+ bool 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 = false;
+ other_data = 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 = true;
+ } else {
+ /*
+ * Look for active extant "other data".
+ *
+ * "Other data" is any rdataset whose type is not
+ * KEY, NSEC, SIG or RRSIG.
+ */
+ rdtype = RBTDB_RDATATYPE_BASE(header->type);
+ if (rdtype != dns_rdatatype_key &&
+ rdtype != dns_rdatatype_sig &&
+ rdtype != dns_rdatatype_nsec &&
+ rdtype != dns_rdatatype_rrsig) {
+ /*
+ * 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 = true;
+ }
+ }
+ }
+
+ if (cname && other_data)
+ return (true);
+
+ return (false);
+}
+
+static isc_result_t
+resign_insert(dns_rbtdb_t *rbtdb, int idx, rdatasetheader_t *newheader) {
+ isc_result_t result;
+
+ INSIST(!IS_CACHE(rbtdb));
+ INSIST(newheader->heap_index == 0);
+ INSIST(!ISC_LINK_LINKED(newheader, link));
+
+ result = isc_heap_insert(rbtdb->heaps[idx], newheader);
+ return (result);
+}
+
+static void
+resign_delete(dns_rbtdb_t *rbtdb, rbtdb_version_t *version,
+ rdatasetheader_t *header)
+{
+ /*
+ * Remove the old header from the heap
+ */
+ if (header != NULL && header->heap_index != 0) {
+ isc_heap_delete(rbtdb->heaps[header->node->locknum],
+ header->heap_index);
+ header->heap_index = 0;
+ if (version != NULL) {
+ new_reference(rbtdb, header->node);
+ ISC_LIST_APPEND(version->resigned_list, header, link);
+ }
+ }
+}
+
+static void
+update_recordsandbytes(bool add, rbtdb_version_t *rbtversion,
+ rdatasetheader_t *header)
+{
+ unsigned char *hdr = (unsigned char *)header;
+ size_t hdrsize = sizeof (*header);
+
+ if (add) {
+ rbtversion->records += dns_rdataslab_count(hdr, hdrsize);
+ rbtversion->bytes += dns_rdataslab_size(hdr, hdrsize);
+ } else {
+ rbtversion->records -= dns_rdataslab_count(hdr, hdrsize);
+ rbtversion->bytes -= dns_rdataslab_size(hdr, hdrsize);
+ }
+}
+
+static isc_result_t
+add32(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion,
+ rdatasetheader_t *newheader, unsigned int options, bool loading,
+ dns_rdataset_t *addedrdataset, isc_stdtime_t now)
+{
+ rbtdb_changed_t *changed = NULL;
+ rdatasetheader_t *topheader, *topheader_prev, *header, *sigheader;
+ unsigned char *merged;
+ isc_result_t result;
+ bool header_nx;
+ bool newheader_nx;
+ bool merge;
+ dns_rdatatype_t rdtype, covers;
+ rbtdb_rdatatype_t negtype, sigtype;
+ 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 = true;
+ } else
+ merge = 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) ? true : false;
+ topheader_prev = NULL;
+ sigheader = NULL;
+ negtype = 0;
+ if (rbtversion == NULL && !newheader_nx) {
+ rdtype = RBTDB_RDATATYPE_BASE(newheader->type);
+ covers = RBTDB_RDATATYPE_EXT(newheader->type);
+ sigtype = RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, covers);
+ if (NEGATIVE(newheader)) {
+ /*
+ * We're adding a negative cache entry.
+ */
+ if (covers == dns_rdatatype_any) {
+ /*
+ * If 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);
+ mark_stale_header(rbtdb, topheader);
+ }
+ goto find_header;
+ }
+ /*
+ * Otherwise look for any RRSIGs of the given
+ * type so they can be marked stale later.
+ */
+ for (topheader = rbtnode->data;
+ topheader != NULL;
+ topheader = topheader->next)
+ if (topheader->type == sigtype)
+ sigheader = topheader;
+ 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. If we're adding an RRSIG, also
+ * check for an extant non-stale NODATA ncache
+ * entry which covers the same type as the RRSIG.
+ */
+ for (topheader = rbtnode->data;
+ topheader != NULL;
+ topheader = topheader->next) {
+ if ((topheader->type ==
+ RBTDB_RDATATYPE_NCACHEANY) ||
+ (newheader->type == sigtype &&
+ topheader->type ==
+ RBTDB_RDATATYPE_VALUE(0, covers))) {
+ break;
+ }
+ }
+ if (topheader != NULL && EXISTS(topheader) &&
+ ACTIVE(topheader, 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
+ * ncache entry.
+ */
+ set_ttl(rbtdb, topheader, 0);
+ mark_stale_header(rbtdb, topheader);
+ 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) ? true : 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 &&
+ (ACTIVE(header, 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 = false;
+
+ /*
+ * If 'merge' is 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;
+ init_rdataset(rbtdb, newheader);
+ update_newheader(newheader, header);
+ if (loading && RESIGN(newheader) &&
+ RESIGN(header) &&
+ resign_sooner(header, newheader))
+ {
+ newheader->resign = header->resign;
+ newheader->resign_lsb =
+ header->resign_lsb;
+ }
+ } 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) && ACTIVE(header, 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 we have will be replacing a NS RRset force its TTL
+ * to be no more than the current NS RRset's TTL. This
+ * ensures the delegations that are withdrawn are honoured.
+ */
+ if (IS_CACHE(rbtdb) && ACTIVE(header, now) &&
+ header->type == dns_rdatatype_ns &&
+ !header_nx && !newheader_nx &&
+ header->trust <= newheader->trust) {
+ if (newheader->rdh_ttl > header->rdh_ttl) {
+ newheader->rdh_ttl = header->rdh_ttl;
+ }
+ }
+ if (IS_CACHE(rbtdb) && ACTIVE(header, now) &&
+ (options & DNS_DBADD_PREFETCH) == 0 &&
+ (header->type == dns_rdatatype_a ||
+ header->type == dns_rdatatype_aaaa ||
+ header->type == dns_rdatatype_ds ||
+ header->type == RBTDB_RDATATYPE_SIGDDS) &&
+ !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 (loading) {
+ newheader->down = NULL;
+ idx = newheader->node->locknum;
+ if (IS_CACHE(rbtdb)) {
+ if (ZEROTTL(newheader))
+ ISC_LIST_APPEND(rbtdb->rdatasets[idx],
+ newheader, link);
+ else
+ ISC_LIST_PREPEND(rbtdb->rdatasets[idx],
+ newheader, link);
+ INSIST(rbtdb->heaps != NULL);
+ result = isc_heap_insert(rbtdb->heaps[idx],
+ newheader);
+ if (result != ISC_R_SUCCESS) {
+ free_rdataset(rbtdb,
+ rbtdb->common.mctx,
+ newheader);
+ return (result);
+ }
+ } else if (RESIGN(newheader)) {
+ result = resign_insert(rbtdb, idx, newheader);
+ if (result != ISC_R_SUCCESS) {
+ free_rdataset(rbtdb,
+ rbtdb->common.mctx,
+ newheader);
+ return (result);
+ }
+ /*
+ * Don't call resign_delete as we don't need
+ * to reverse the delete. The free_rdataset
+ * call below will clean up the heap entry.
+ */
+ }
+
+ /*
+ * 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.
+ */
+ if (topheader_prev != NULL)
+ topheader_prev->next = newheader;
+ else
+ rbtnode->data = newheader;
+ newheader->next = topheader->next;
+ if (rbtversion != NULL && !header_nx) {
+ RWLOCK(&rbtversion->rwlock,
+ isc_rwlocktype_write);
+ update_recordsandbytes(false, rbtversion,
+ header);
+ RWUNLOCK(&rbtversion->rwlock,
+ isc_rwlocktype_write);
+ }
+ free_rdataset(rbtdb, rbtdb->common.mctx, header);
+ } else {
+ idx = newheader->node->locknum;
+ if (IS_CACHE(rbtdb)) {
+ INSIST(rbtdb->heaps != NULL);
+ result = isc_heap_insert(rbtdb->heaps[idx],
+ newheader);
+ if (result != ISC_R_SUCCESS) {
+ free_rdataset(rbtdb,
+ rbtdb->common.mctx,
+ newheader);
+ return (result);
+ }
+ if (ZEROTTL(newheader))
+ ISC_LIST_APPEND(rbtdb->rdatasets[idx],
+ newheader, link);
+ else
+ ISC_LIST_PREPEND(rbtdb->rdatasets[idx],
+ newheader, link);
+ } else if (RESIGN(newheader)) {
+ result = resign_insert(rbtdb, idx, newheader);
+ if (result != ISC_R_SUCCESS) {
+ free_rdataset(rbtdb,
+ rbtdb->common.mctx,
+ newheader);
+ return (result);
+ }
+ resign_delete(rbtdb, rbtversion, header);
+ }
+ 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 = true;
+ if (rbtversion == NULL) {
+ set_ttl(rbtdb, header, 0);
+ mark_stale_header(rbtdb, header);
+ if (sigheader != NULL) {
+ set_ttl(rbtdb, sigheader, 0);
+ mark_stale_header(rbtdb, sigheader);
+ }
+ }
+ if (rbtversion != NULL && !header_nx) {
+ RWLOCK(&rbtversion->rwlock,
+ isc_rwlocktype_write);
+ update_recordsandbytes(false, rbtversion,
+ header);
+ RWUNLOCK(&rbtversion->rwlock,
+ isc_rwlocktype_write);
+ }
+ }
+ } 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);
+ }
+
+ idx = newheader->node->locknum;
+ if (IS_CACHE(rbtdb)) {
+ result = isc_heap_insert(rbtdb->heaps[idx], newheader);
+ if (result != ISC_R_SUCCESS) {
+ free_rdataset(rbtdb, rbtdb->common.mctx,
+ newheader);
+ return (result);
+ }
+ if (ZEROTTL(newheader))
+ ISC_LIST_APPEND(rbtdb->rdatasets[idx],
+ newheader, link);
+ else
+ ISC_LIST_PREPEND(rbtdb->rdatasets[idx],
+ newheader, link);
+ } else if (RESIGN(newheader)) {
+ result = resign_insert(rbtdb, idx, newheader);
+ if (result != ISC_R_SUCCESS) {
+ free_rdataset(rbtdb, rbtdb->common.mctx,
+ newheader);
+ return (result);
+ }
+ resign_delete(rbtdb, rbtversion, header);
+ }
+
+ 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 = true;
+ } else {
+ /*
+ * No rdatasets of the given type exist at the node.
+ */
+ newheader->next = rbtnode->data;
+ newheader->down = NULL;
+ rbtnode->data = newheader;
+ }
+ }
+
+ if (rbtversion != NULL && !newheader_nx) {
+ RWLOCK(&rbtversion->rwlock, isc_rwlocktype_write);
+ update_recordsandbytes(true, rbtversion, newheader);
+ RWUNLOCK(&rbtversion->rwlock, isc_rwlocktype_write);
+ }
+
+ /*
+ * 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 bool
+delegating_type(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
+ rbtdb_rdatatype_t type)
+{
+ if (IS_CACHE(rbtdb)) {
+ if (type == dns_rdatatype_dname)
+ return (true);
+ else
+ return (false);
+ } else if (type == dns_rdatatype_dname ||
+ (type == dns_rdatatype_ns &&
+ (node != rbtdb->origin_node || IS_STUB(rbtdb))))
+ return (true);
+ return (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);
+ if (noqname != NULL)
+ 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);
+ if (closest != NULL)
+ 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;
+ bool delegating;
+ bool newnsec;
+ bool tree_locked = false;
+ bool cache_is_overmem = false;
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+ INSIST(rbtversion == NULL || rbtversion->rbtdb == rbtdb);
+
+ if (rbtdb->common.methods == &zone_methods)
+ REQUIRE(((rbtnode->nsec == DNS_RBT_NSEC_NSEC3 &&
+ (rdataset->type == dns_rdatatype_nsec3 ||
+ rdataset->covers == dns_rdatatype_nsec3)) ||
+ (rbtnode->nsec != DNS_RBT_NSEC_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);
+
+ name = dns_fixedname_initname(&fixed);
+ RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+ dns_rbt_fullnamefromnode(node, name);
+ RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+ dns_rdataset_getownercase(rdataset, name);
+
+ newheader = (rdatasetheader_t *)region.base;
+ init_rdataset(rbtdb, newheader);
+ setownercase(newheader, name);
+ set_ttl(rbtdb, newheader, rdataset->ttl + now);
+ newheader->type = RBTDB_RDATATYPE_VALUE(rdataset->type,
+ rdataset->covers);
+ newheader->attributes = 0;
+ if (rdataset->ttl == 0U)
+ newheader->attributes |= RDATASET_ATTR_ZEROTTL;
+ 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 = (isc_stdtime_t)
+ (dns_time64_from32(rdataset->resign) >> 1);
+ newheader->resign_lsb = rdataset->resign & 0x1;
+ } else {
+ newheader->resign = 0;
+ newheader->resign_lsb = 0;
+ }
+ } else {
+ newheader->serial = 1;
+ newheader->resign = 0;
+ newheader->resign_lsb = 0;
+ if ((rdataset->attributes & DNS_RDATASETATTR_PREFETCH) != 0)
+ newheader->attributes |= RDATASET_ATTR_PREFETCH;
+ if ((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0)
+ newheader->attributes |= RDATASET_ATTR_NEGATIVE;
+ 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 = true;
+ else
+ delegating = false;
+
+ /*
+ * Add to the auxiliary NSEC tree if we're adding an NSEC record.
+ */
+ if (rbtnode->nsec != DNS_RBT_NSEC_HAS_NSEC &&
+ rdataset->type == dns_rdatatype_nsec)
+ newnsec = true;
+ else
+ newnsec = false;
+
+ /*
+ * If we're adding a delegation type, adding to the auxiliary NSEC tree,
+ * 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 (IS_CACHE(rbtdb) && isc_mem_isovermem(rbtdb->common.mctx))
+ cache_is_overmem = true;
+ if (delegating || newnsec || cache_is_overmem) {
+ tree_locked = true;
+ RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
+ }
+
+ if (cache_is_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, 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,
+ expire_ttl);
+
+ /*
+ * 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 && !newnsec) {
+ RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
+ tree_locked = false;
+ }
+ }
+
+ result = ISC_R_SUCCESS;
+ if (newnsec) {
+ dns_rbtnode_t *nsecnode;
+
+ dns_rbt_fullnamefromnode(rbtnode, name);
+ nsecnode = NULL;
+ result = dns_rbt_addnode(rbtdb->nsec, name, &nsecnode);
+ if (result == ISC_R_SUCCESS) {
+ nsecnode->nsec = DNS_RBT_NSEC_NSEC;
+ rbtnode->nsec = DNS_RBT_NSEC_HAS_NSEC;
+ } else if (result == ISC_R_EXISTS) {
+ rbtnode->nsec = DNS_RBT_NSEC_HAS_NSEC;
+ result = ISC_R_SUCCESS;
+ }
+ }
+
+ if (result == ISC_R_SUCCESS)
+ result = add32(rbtdb, rbtnode, rbtversion, newheader, options,
+ 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 deferred 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));
+ REQUIRE(rbtversion != NULL && rbtversion->rbtdb == rbtdb);
+
+ if (rbtdb->common.methods == &zone_methods)
+ REQUIRE(((rbtnode->nsec == DNS_RBT_NSEC_NSEC3 &&
+ (rdataset->type == dns_rdatatype_nsec3 ||
+ rdataset->covers == dns_rdatatype_nsec3)) ||
+ (rbtnode->nsec != DNS_RBT_NSEC_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 = (isc_stdtime_t)
+ (dns_time64_from32(rdataset->resign) >> 1);
+ newheader->resign_lsb = rdataset->resign & 0x1;
+ } else {
+ newheader->resign = 0;
+ newheader->resign_lsb = 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);
+ update_newheader(newheader, header);
+ if (RESIGN(header)) {
+ newheader->attributes |= RDATASET_ATTR_RESIGN;
+ newheader->resign = header->resign;
+ newheader->resign_lsb = header->resign_lsb;
+ result = resign_insert(rbtdb, rbtnode->locknum,
+ newheader);
+ if (result != ISC_R_SUCCESS) {
+ free_rdataset(rbtdb,
+ rbtdb->common.mctx,
+ newheader);
+ goto unlock;
+ }
+ }
+ /*
+ * 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;
+ update_recordsandbytes(true, rbtversion, newheader);
+ } 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;
+ }
+ init_rdataset(rbtdb, newheader);
+ 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->resign_lsb = 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);
+ update_recordsandbytes(false, rbtversion, header);
+ 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 = true;
+ resign_delete(rbtdb, rbtversion, header);
+ } 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);
+
+ if (result == DNS_R_NXRRSET && newrdataset != NULL &&
+ (options & DNS_DBSUB_WANTOLD) != 0)
+ bind_rdataset(rbtdb, rbtnode, header, 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 deferred 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));
+ INSIST(rbtversion == NULL || rbtversion->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);
+ init_rdataset(rbtdb, newheader);
+ 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 = add32(rbtdb, rbtnode, rbtversion, newheader, DNS_DBADD_FORCE,
+ 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 deferred until closeversion() is called.
+ */
+ if (result == ISC_R_SUCCESS && version == NULL && !IS_CACHE(rbtdb))
+ iszonesecure(db, rbtdb->current_version, rbtdb->origin_node);
+
+ return (result);
+}
+
+/*
+ * load a non-NSEC3 node in the main tree and optionally to the auxiliary NSEC
+ */
+static isc_result_t
+loadnode(dns_rbtdb_t *rbtdb, dns_name_t *name, dns_rbtnode_t **nodep,
+ bool hasnsec)
+{
+ isc_result_t noderesult, rpzresult, nsecresult, tmpresult;
+ dns_rbtnode_t *nsecnode = NULL, *node = NULL;
+
+ noderesult = dns_rbt_addnode(rbtdb->tree, name, &node);
+ if (rbtdb->rpzs != NULL &&
+ (noderesult == ISC_R_SUCCESS || noderesult == ISC_R_EXISTS)) {
+ rpzresult = dns_rpz_add(rbtdb->load_rpzs, rbtdb->rpz_num,
+ name);
+ if (rpzresult == ISC_R_SUCCESS) {
+ node->rpz = 1;
+ } else if (noderesult != ISC_R_EXISTS) {
+ /*
+ * Remove the node we just added above.
+ */
+ tmpresult = dns_rbt_deletenode(rbtdb->tree, node,
+ false);
+ if (tmpresult != ISC_R_SUCCESS)
+ isc_log_write(dns_lctx,
+ DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_CACHE,
+ ISC_LOG_WARNING,
+ "loading_addrdataset: "
+ "dns_rbt_deletenode: %s after "
+ "dns_rbt_addnode(NSEC): %s",
+ isc_result_totext(tmpresult),
+ isc_result_totext(ISC_R_SUCCESS));
+ noderesult = rpzresult;
+ }
+ }
+ if (!hasnsec)
+ goto done;
+ if (noderesult == ISC_R_EXISTS) {
+ /*
+ * Add a node to the auxiliary NSEC tree for an old node
+ * just now getting an NSEC record.
+ */
+ if (node->nsec == DNS_RBT_NSEC_HAS_NSEC)
+ goto done;
+ } else if (noderesult != ISC_R_SUCCESS)
+ goto done;
+
+ /*
+ * Build the auxiliary tree for NSECs as we go.
+ * This tree speeds searches for closest NSECs that would otherwise
+ * need to examine many irrelevant nodes in large TLDs.
+ *
+ * Add nodes to the auxiliary tree after corresponding nodes have
+ * been added to the main tree.
+ */
+ nsecresult = dns_rbt_addnode(rbtdb->nsec, name, &nsecnode);
+ if (nsecresult == ISC_R_SUCCESS) {
+ nsecnode->nsec = DNS_RBT_NSEC_NSEC;
+ node->nsec = DNS_RBT_NSEC_HAS_NSEC;
+ goto done;
+ }
+
+ if (nsecresult == ISC_R_EXISTS) {
+#if 1 /* 0 */
+ isc_log_write(dns_lctx,
+ DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_CACHE,
+ ISC_LOG_WARNING,
+ "addnode: NSEC node already exists");
+#endif
+ node->nsec = DNS_RBT_NSEC_HAS_NSEC;
+ goto done;
+ }
+
+ if (noderesult == ISC_R_SUCCESS) {
+ unsigned int node_has_rpz;
+
+ /*
+ * Remove the node we just added above.
+ * dns_rbt_deletenode() may keep the node if it has a
+ * down pointer, but we mustn't call dns_rpz_delete() on
+ * it again.
+ */
+ node_has_rpz = node->rpz;
+ node->rpz = 0;
+ tmpresult = dns_rbt_deletenode(rbtdb->tree, node, false);
+ if (tmpresult == ISC_R_SUCCESS) {
+ /*
+ * Clean rpz entries added above.
+ */
+ if (rbtdb->rpzs != NULL && node_has_rpz)
+ dns_rpz_delete(rbtdb->load_rpzs,
+ rbtdb->rpz_num, name);
+ } else {
+ isc_log_write(dns_lctx,
+ DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_CACHE,
+ ISC_LOG_WARNING,
+ "loading_addrdataset: "
+ "dns_rbt_deletenode: %s after "
+ "dns_rbt_addnode(NSEC): %s",
+ isc_result_totext(tmpresult),
+ isc_result_totext(noderesult));
+ }
+ }
+
+ /*
+ * Set the error condition to be returned.
+ */
+ noderesult = nsecresult;
+
+ done:
+ if (noderesult == ISC_R_SUCCESS || noderesult == ISC_R_EXISTS)
+ *nodep = node;
+
+ return (noderesult);
+}
+
+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;
+
+ REQUIRE(rdataset->rdclass == rbtdb->common.rdclass);
+
+ /*
+ * 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->nsec = DNS_RBT_NSEC_NSEC3;
+ } else if (rdataset->type == dns_rdatatype_nsec) {
+ result = loadnode(rbtdb, name, &node, true);
+ } else {
+ result = loadnode(rbtdb, name, &node, false);
+ }
+ if (result != ISC_R_SUCCESS && result != ISC_R_EXISTS)
+ return (result);
+ if (result == ISC_R_SUCCESS) {
+ 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, 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;
+ setownercase(newheader, name);
+
+ if ((rdataset->attributes & DNS_RDATASETATTR_RESIGN) != 0) {
+ newheader->attributes |= RDATASET_ATTR_RESIGN;
+ newheader->resign = (isc_stdtime_t)
+ (dns_time64_from32(rdataset->resign) >> 1);
+ newheader->resign_lsb = rdataset->resign & 0x1;
+ } else {
+ newheader->resign = 0;
+ newheader->resign_lsb = 0;
+ }
+
+ result = add32(rbtdb, node, rbtdb->current_version, newheader,
+ DNS_DBADD_MERGE, 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
+rbt_datafixer(dns_rbtnode_t *rbtnode, void *base, size_t filesize,
+ void *arg, uint64_t *crc)
+{
+ isc_result_t result;
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *) arg;
+ rdatasetheader_t *header;
+ unsigned char *limit = ((unsigned char *) base) + filesize;
+ unsigned char *p;
+ size_t size;
+ unsigned int count;
+
+ REQUIRE(rbtnode != NULL);
+
+ for (header = rbtnode->data; header != NULL; header = header->next) {
+ p = (unsigned char *) header;
+
+ size = dns_rdataslab_size(p, sizeof(*header));
+ count = dns_rdataslab_count(p, sizeof(*header));;
+ rbtdb->current_version->records += count;
+ rbtdb->current_version->bytes += size;
+ isc_crc64_update(crc, p, size);
+#ifdef DEBUG
+ hexdump("hashing header", p, sizeof(rdatasetheader_t));
+ hexdump("hashing slab", p + sizeof(rdatasetheader_t),
+ size - sizeof(rdatasetheader_t));
+#endif
+ header->serial = 1;
+ header->is_mmapped = 1;
+ header->node = rbtnode;
+ header->node_is_relative = 0;
+
+ if (rbtdb != NULL && RESIGN(header) &&
+ (header->resign != 0 || header->resign_lsb != 0))
+ {
+ int idx = header->node->locknum;
+ result = isc_heap_insert(rbtdb->heaps[idx], header);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+
+ if (header->next != NULL) {
+ size_t cooked = dns_rbt_serialize_align(size);
+ if ((uintptr_t)header->next !=
+ (p - (unsigned char *)base) + cooked)
+ return (ISC_R_INVALIDFILE);
+ header->next = (rdatasetheader_t *)(p + cooked);
+ header->next_is_relative = 0;
+ if ((header->next < (rdatasetheader_t *) base) ||
+ (header->next > (rdatasetheader_t *) limit))
+ return (ISC_R_INVALIDFILE);
+ }
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Load the RBT database from the image in 'f'
+ */
+static isc_result_t
+deserialize32(void *arg, FILE *f, off_t offset) {
+ isc_result_t result;
+ rbtdb_load_t *loadctx = arg;
+ dns_rbtdb_t *rbtdb = loadctx->rbtdb;
+ rbtdb_file_header_t *header;
+ int fd;
+ off_t filesize = 0;
+ char *base;
+ dns_rbt_t *tree = NULL, *nsec = NULL, *nsec3 = NULL;
+ int protect, flags;
+ dns_rbtnode_t *origin_node = NULL;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+
+ /*
+ * TODO CKB: since this is read-write (had to be to add nodes later)
+ * we will need to lock the file or the nodes in it before modifying
+ * the nodes in the file.
+ */
+
+ /* Map in the whole file in one go */
+ fd = fileno(f);
+ isc_file_getsizefd(fd, &filesize);
+ protect = PROT_READ|PROT_WRITE;
+ flags = MAP_PRIVATE;
+#ifdef MAP_FILE
+ flags |= MAP_FILE;
+#endif
+
+ base = isc_file_mmap(NULL, filesize, protect, flags, fd, 0);
+ if (base == NULL || base == MAP_FAILED) {
+ return (ISC_R_FAILURE);
+ }
+
+ header = (rbtdb_file_header_t *)(base + offset);
+ if (!match_header_version(header)) {
+ result = ISC_R_INVALIDFILE;
+ goto cleanup;
+ }
+
+ if (header->tree != 0) {
+ result = dns_rbt_deserialize_tree(base, filesize,
+ (off_t) header->tree,
+ rbtdb->common.mctx,
+ delete_callback, rbtdb,
+ rbt_datafixer, rbtdb,
+ NULL, &tree);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = dns_rbt_findnode(tree, &rbtdb->common.origin, NULL,
+ &origin_node, NULL,
+ DNS_RBTFIND_EMPTYDATA, NULL, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+
+ if (header->nsec != 0) {
+ result = dns_rbt_deserialize_tree(base, filesize,
+ (off_t) header->nsec,
+ rbtdb->common.mctx,
+ delete_callback, rbtdb,
+ rbt_datafixer, rbtdb,
+ NULL, &nsec);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+
+ if (header->nsec3 != 0) {
+ result = dns_rbt_deserialize_tree(base, filesize,
+ (off_t) header->nsec3,
+ rbtdb->common.mctx,
+ delete_callback, rbtdb,
+ rbt_datafixer, rbtdb,
+ NULL, &nsec3);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+
+ /*
+ * We have a successfully loaded all the rbt trees now update
+ * rbtdb to use them.
+ */
+
+ rbtdb->mmap_location = base;
+ rbtdb->mmap_size = (size_t) filesize;
+
+ if (tree != NULL) {
+ dns_rbt_destroy(&rbtdb->tree);
+ rbtdb->tree = tree;
+ rbtdb->origin_node = origin_node;
+ }
+
+ if (nsec != NULL) {
+ dns_rbt_destroy(&rbtdb->nsec);
+ rbtdb->nsec = nsec;
+ }
+
+ if (nsec3 != NULL) {
+ dns_rbt_destroy(&rbtdb->nsec3);
+ rbtdb->nsec3 = nsec3;
+ }
+
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (tree != NULL)
+ dns_rbt_destroy(&tree);
+ if (nsec != NULL)
+ dns_rbt_destroy(&nsec);
+ if (nsec3 != NULL)
+ dns_rbt_destroy(&nsec3);
+ isc_file_munmap(base, (size_t) filesize);
+ return (result);
+}
+
+static isc_result_t
+beginload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
+ rbtdb_load_t *loadctx;
+ dns_rbtdb_t *rbtdb;
+ rbtdb = (dns_rbtdb_t *)db;
+
+ REQUIRE(DNS_CALLBACK_VALID(callbacks));
+ 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);
+
+ if (rbtdb->rpzs != NULL) {
+ isc_result_t result;
+
+ result = dns_rpz_beginload(&rbtdb->load_rpzs,
+ rbtdb->rpzs, rbtdb->rpz_num);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(rbtdb->common.mctx, loadctx,
+ sizeof(*loadctx));
+ return (result);
+ }
+ }
+
+ REQUIRE((rbtdb->attributes & (RBTDB_ATTR_LOADED|RBTDB_ATTR_LOADING))
+ == 0);
+ rbtdb->attributes |= RBTDB_ATTR_LOADING;
+
+ RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write);
+
+ callbacks->add = loading_addrdataset;
+ callbacks->add_private = loadctx;
+ callbacks->deserialize = deserialize32;
+ callbacks->deserialize_private = loadctx;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+endload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
+ rbtdb_load_t *loadctx;
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+ REQUIRE(DNS_CALLBACK_VALID(callbacks));
+ loadctx = callbacks->add_private;
+ REQUIRE(loadctx != NULL);
+ 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) && rbtdb->origin_node != NULL)
+ iszonesecure(db, rbtdb->current_version, rbtdb->origin_node);
+
+ callbacks->add = NULL;
+ callbacks->add_private = NULL;
+ callbacks->deserialize = NULL;
+ callbacks->deserialize_private = NULL;
+
+ isc_mem_put(rbtdb->common.mctx, loadctx, sizeof(*loadctx));
+
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * helper function to handle writing out the rdataset data pointed to
+ * by the void *data pointer in the dns_rbtnode
+ */
+static isc_result_t
+rbt_datawriter(FILE *rbtfile, unsigned char *data, void *arg,
+ uint64_t *crc)
+{
+ rbtdb_version_t *version = (rbtdb_version_t *) arg;
+ rbtdb_serial_t serial;
+ rdatasetheader_t newheader;
+ rdatasetheader_t *header = (rdatasetheader_t *) data, *next;
+ off_t where;
+ size_t cooked, size;
+ unsigned char *p;
+ isc_result_t result = ISC_R_SUCCESS;
+ char pad[sizeof(char *)];
+ uintptr_t off;
+
+ REQUIRE(rbtfile != NULL);
+ REQUIRE(data != NULL);
+ REQUIRE(version != NULL);
+
+ serial = version->serial;
+
+ for (; header != NULL; header = next) {
+ next = header->next;
+ do {
+ if (header->serial <= serial && !IGNORE(header)) {
+ if (NONEXISTENT(header))
+ header = NULL;
+ break;
+ } else
+ header = header->down;
+ } while (header != NULL);
+
+ if (header == NULL)
+ continue;
+
+ CHECK(isc_stdio_tell(rbtfile, &where));
+ size = dns_rdataslab_size((unsigned char *) header,
+ sizeof(rdatasetheader_t));
+
+ p = (unsigned char *) header;
+ memmove(&newheader, p, sizeof(rdatasetheader_t));
+ newheader.down = NULL;
+ newheader.next = NULL;
+ off = where;
+ if ((off_t)off != where)
+ return (ISC_R_RANGE);
+ newheader.node = (dns_rbtnode_t *) off;
+ newheader.node_is_relative = 1;
+ newheader.serial = 1;
+
+ /*
+ * Round size up to the next pointer sized offset so it
+ * will be properly aligned when read back in.
+ */
+ cooked = dns_rbt_serialize_align(size);
+ if (next != NULL) {
+ newheader.next = (rdatasetheader_t *) (off + cooked);
+ newheader.next_is_relative = 1;
+ }
+
+#ifdef DEBUG
+ hexdump("writing header", (unsigned char *) &newheader,
+ sizeof(rdatasetheader_t));
+ hexdump("writing slab", p + sizeof(rdatasetheader_t),
+ size - sizeof(rdatasetheader_t));
+#endif
+ isc_crc64_update(crc, (unsigned char *) &newheader,
+ sizeof(rdatasetheader_t));
+ CHECK(isc_stdio_write(&newheader, sizeof(rdatasetheader_t), 1,
+ rbtfile, NULL));
+
+ isc_crc64_update(crc, p + sizeof(rdatasetheader_t),
+ size - sizeof(rdatasetheader_t));
+ CHECK(isc_stdio_write(p + sizeof(rdatasetheader_t),
+ size - sizeof(rdatasetheader_t), 1,
+ rbtfile, NULL));
+ /*
+ * Pad to force alignment.
+ */
+ if (size != (size_t) cooked) {
+ memset(pad, 0, sizeof(pad));
+ CHECK(isc_stdio_write(pad, cooked - size, 1,
+ rbtfile, NULL));
+ }
+ }
+
+ failure:
+ return (result);
+}
+
+/*
+ * Write out a zeroed header as a placeholder. Doing this ensures
+ * that the file will not read while it is partially written, should
+ * writing fail or be interrupted.
+ */
+static isc_result_t
+rbtdb_zero_header(FILE *rbtfile) {
+ char buffer[RBTDB_HEADER_LENGTH];
+ isc_result_t result;
+
+ memset(buffer, 0, RBTDB_HEADER_LENGTH);
+ result = isc_stdio_write(buffer, 1, RBTDB_HEADER_LENGTH, rbtfile, NULL);
+ fflush(rbtfile);
+
+ return (result);
+}
+
+static isc_once_t once = ISC_ONCE_INIT;
+
+static void
+init_file_version(void) {
+ int n;
+
+ memset(FILE_VERSION, 0, sizeof(FILE_VERSION));
+ n = snprintf(FILE_VERSION, sizeof(FILE_VERSION),
+ "RBTDB Image %s %s", dns_major, dns_mapapi);
+ INSIST(n > 0 && (unsigned int)n < sizeof(FILE_VERSION));
+}
+
+/*
+ * Write the file header out, recording the locations of the three
+ * RBT's used in the rbtdb: tree, nsec, and nsec3, and including NodeDump
+ * version information and any information stored in the rbtdb object
+ * itself that should be stored here.
+ */
+static isc_result_t
+rbtdb_write_header(FILE *rbtfile, off_t tree_location, off_t nsec_location,
+ off_t nsec3_location)
+{
+ rbtdb_file_header_t header;
+ isc_result_t result;
+
+ RUNTIME_CHECK(isc_once_do(&once, init_file_version) == ISC_R_SUCCESS);
+
+ memset(&header, 0, sizeof(rbtdb_file_header_t));
+ memmove(header.version1, FILE_VERSION, sizeof(header.version1));
+ memmove(header.version2, FILE_VERSION, sizeof(header.version2));
+ header.ptrsize = (uint32_t) sizeof(void *);
+ header.bigendian = (1 == htonl(1)) ? 1 : 0;
+ header.tree = (uint64_t) tree_location;
+ header.nsec = (uint64_t) nsec_location;
+ header.nsec3 = (uint64_t) nsec3_location;
+ result = isc_stdio_write(&header, 1, sizeof(rbtdb_file_header_t),
+ rbtfile, NULL);
+ fflush(rbtfile);
+
+ return (result);
+}
+
+static bool
+match_header_version(rbtdb_file_header_t *header) {
+ RUNTIME_CHECK(isc_once_do(&once, init_file_version) == ISC_R_SUCCESS);
+
+ if (memcmp(header->version1, FILE_VERSION,
+ sizeof(header->version1)) != 0 ||
+ memcmp(header->version2, FILE_VERSION,
+ sizeof(header->version1)) != 0)
+ {
+ return (false);
+ }
+
+ return (true);
+}
+
+static isc_result_t
+serialize(dns_db_t *db, dns_dbversion_t *ver, FILE *rbtfile) {
+ rbtdb_version_t *version = (rbtdb_version_t *) ver;
+ dns_rbtdb_t *rbtdb;
+ isc_result_t result;
+ off_t tree_location, nsec_location, nsec3_location, header_location;
+
+ rbtdb = (dns_rbtdb_t *)db;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+ REQUIRE(rbtfile != NULL);
+
+ /* Ensure we're writing to a plain file */
+ CHECK(isc_file_isplainfilefd(fileno(rbtfile)));
+
+ /*
+ * first, write out a zeroed header to store rbtdb information
+ *
+ * then for each of the three trees, store the current position
+ * in the file and call dns_rbt_serialize_tree
+ *
+ * finally, write out the rbtdb header, storing the locations of the
+ * rbtheaders
+ *
+ * NOTE: need to do something better with the return codes, &= will
+ * not work.
+ */
+ CHECK(isc_stdio_tell(rbtfile, &header_location));
+ CHECK(rbtdb_zero_header(rbtfile));
+ CHECK(dns_rbt_serialize_tree(rbtfile, rbtdb->tree, rbt_datawriter,
+ version, &tree_location));
+ CHECK(dns_rbt_serialize_tree(rbtfile, rbtdb->nsec, rbt_datawriter,
+ version, &nsec_location));
+ CHECK(dns_rbt_serialize_tree(rbtfile, rbtdb->nsec3, rbt_datawriter,
+ version, &nsec3_location));
+
+ CHECK(isc_stdio_seek(rbtfile, header_location, SEEK_SET));
+ CHECK(rbtdb_write_header(rbtfile, tree_location, nsec_location,
+ nsec3_location));
+ failure:
+ return (result);
+}
+
+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_version_t *rbtversion = version;
+
+ rbtdb = (dns_rbtdb_t *)db;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+ INSIST(rbtversion == NULL || rbtversion->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;
+ unsigned int locknum;
+
+ current = data;
+ locknum = current->node->locknum;
+ NODE_LOCK(&rbtdb->node_locks[locknum].lock, isc_rwlocktype_write);
+ while (current != NULL) {
+ next = current->next;
+ free_rdataset(rbtdb, rbtdb->common.mctx, current);
+ current = next;
+ }
+ NODE_UNLOCK(&rbtdb->node_locks[locknum].lock, isc_rwlocktype_write);
+}
+
+static bool
+issecure(dns_db_t *db) {
+ dns_rbtdb_t *rbtdb;
+ bool secure;
+
+ rbtdb = (dns_rbtdb_t *)db;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+
+ RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+ secure = (rbtdb->current_version->secure == dns_db_secure);
+ RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+
+ return (secure);
+}
+
+static bool
+isdnssec(dns_db_t *db) {
+ dns_rbtdb_t *rbtdb;
+ bool dnssec;
+
+ rbtdb = (dns_rbtdb_t *)db;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+
+ RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+ dnssec = (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 size_t
+hashsize(dns_db_t *db) {
+ dns_rbtdb_t *rbtdb;
+ size_t size;
+
+ rbtdb = (dns_rbtdb_t *)db;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+
+ RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+ size = dns_rbt_hashsize(rbtdb->tree);
+ RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+
+ return (size);
+}
+
+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 bool
+ispersistent(dns_db_t *db) {
+ UNUSED(db);
+ return (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,
+ uint8_t *flags, 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));
+ INSIST(rbtversion == NULL || rbtversion->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 != NULL) {
+ REQUIRE(*salt_length >= rbtversion->salt_length);
+ memmove(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
+getsize(dns_db_t *db, dns_dbversion_t *version, uint64_t *records,
+ uint64_t *bytes)
+{
+ dns_rbtdb_t *rbtdb;
+ isc_result_t result = ISC_R_SUCCESS;
+ rbtdb_version_t *rbtversion = version;
+
+ rbtdb = (dns_rbtdb_t *)db;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+ INSIST(rbtversion == NULL || rbtversion->rbtdb == rbtdb);
+
+ if (rbtversion == NULL)
+ rbtversion = rbtdb->current_version;
+
+ RWLOCK(&rbtversion->rwlock, isc_rwlocktype_read);
+ if (records != NULL)
+ *records = rbtversion->records;
+
+ if (bytes != NULL)
+ *bytes = rbtversion->bytes;
+ RWUNLOCK(&rbtversion->rwlock, 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_result_t result = ISC_R_SUCCESS;
+ rdatasetheader_t *header, oldheader;
+
+ 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);
+
+ oldheader = *header;
+ /*
+ * Only break the heap invariant (by adjusting resign and resign_lsb)
+ * if we are going to be restoring it by calling isc_heap_increased
+ * or isc_heap_decreased.
+ */
+ if (resign != 0) {
+ header->resign =
+ (isc_stdtime_t)(dns_time64_from32(resign) >> 1);
+ header->resign_lsb = resign & 0x1;
+ }
+ 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_sooner(header, &oldheader)) {
+ isc_heap_increased(rbtdb->heaps[header->node->locknum],
+ header->heap_index);
+ } else if (resign_sooner(&oldheader, header)) {
+ isc_heap_decreased(rbtdb->heaps[header->node->locknum],
+ header->heap_index);
+ }
+ } else if (resign != 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;
+ unsigned int locknum;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+
+ RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+
+ for (i = 0; i < rbtdb->node_lock_count; i++) {
+ NODE_LOCK(&rbtdb->node_locks[i].lock, isc_rwlocktype_read);
+ this = isc_heap_element(rbtdb->heaps[i], 1);
+ if (this == NULL) {
+ NODE_UNLOCK(&rbtdb->node_locks[i].lock,
+ isc_rwlocktype_read);
+ continue;
+ }
+ if (header == NULL)
+ header = this;
+ else if (resign_sooner(this, header)) {
+ locknum = header->node->locknum;
+ NODE_UNLOCK(&rbtdb->node_locks[locknum].lock,
+ isc_rwlocktype_read);
+ header = this;
+ } else
+ NODE_UNLOCK(&rbtdb->node_locks[i].lock,
+ isc_rwlocktype_read);
+ }
+
+ if (header == NULL)
+ goto unlock;
+
+ 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:
+ RWUNLOCK(&rbtdb->tree_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(rdataset->methods == &rdataset_methods);
+ REQUIRE(rbtdb->future_version == rbtversion);
+ REQUIRE(rbtversion != NULL);
+ REQUIRE(rbtversion->writer);
+ REQUIRE(rbtversion->rbtdb == rbtdb);
+
+ node = rdataset->private2;
+ INSIST(node != NULL);
+ header = rdataset->private3;
+ INSIST(header != NULL);
+ header--;
+
+ if (header->heap_index == 0)
+ return;
+
+ RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
+ 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.
+ */
+ resign_delete(rbtdb, rbtversion, header);
+ NODE_UNLOCK(&rbtdb->node_locks[node->locknum].lock,
+ isc_rwlocktype_write);
+ RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
+}
+
+static isc_result_t
+setcachestats(dns_db_t *db, isc_stats_t *stats) {
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+ REQUIRE(IS_CACHE(rbtdb)); /* current restriction */
+ REQUIRE(stats != NULL);
+
+ isc_stats_attach(stats, &rbtdb->cachestats);
+ return (ISC_R_SUCCESS);
+}
+
+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 isc_result_t
+nodefullname(dns_db_t *db, dns_dbnode_t *node, dns_name_t *name) {
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+ dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)node;
+ isc_result_t result;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+ REQUIRE(node != NULL);
+ REQUIRE(name != NULL);
+
+ RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+ result = dns_rbt_fullnamefromnode(rbtnode, name);
+ RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+
+ return (result);
+}
+
+static dns_dbmethods_t zone_methods = {
+ attach,
+ detach,
+ beginload,
+ endload,
+ serialize,
+ 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,
+ rpz_attach,
+ rpz_ready,
+ NULL,
+ NULL,
+ NULL,
+ hashsize,
+ nodefullname,
+ getsize
+};
+
+static dns_dbmethods_t cache_methods = {
+ attach,
+ detach,
+ beginload,
+ endload,
+ NULL,
+ 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,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ setcachestats,
+ hashsize,
+ nodefullname,
+ NULL
+};
+
+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;
+ bool (*sooner)(void *, void *);
+ isc_mem_t *hmctx = mctx;
+
+ /* Keep the compiler happy. */
+ UNUSED(driverarg);
+
+ rbtdb = isc_mem_get(mctx, sizeof(*rbtdb));
+ if (rbtdb == NULL)
+ return (ISC_R_NOMEMORY);
+
+ /*
+ * If argv[0] exists, it points to a memory context to use for heap
+ */
+ if (argc != 0)
+ hmctx = (isc_mem_t *) argv[0];
+
+ 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;
+
+ ISC_LIST_INIT(rbtdb->common.update_listeners);
+
+ 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->cachestats = NULL;
+ 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(hmctx, 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(hmctx, 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_destroy(&rbtdb->node_locks[i].references);
+ }
+ goto cleanup_deadnodes;
+ }
+ rbtdb->node_locks[i].exiting = 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);
+ isc_mem_attach(hmctx, &rbtdb->hmctx);
+
+ /*
+ * Must be initialized 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, 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, false, NULL);
+ return (result);
+ }
+
+ result = dns_rbt_create(mctx, delete_callback, rbtdb, &rbtdb->nsec);
+ if (result != ISC_R_SUCCESS) {
+ free_rbtdb(rbtdb, false, NULL);
+ return (result);
+ }
+
+ result = dns_rbt_create(mctx, delete_callback, rbtdb, &rbtdb->nsec3);
+ if (result != ISC_R_SUCCESS) {
+ free_rbtdb(rbtdb, 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, false, NULL);
+ return (result);
+ }
+ INSIST(rbtdb->origin_node != NULL);
+ rbtdb->origin_node->nsec = DNS_RBT_NSEC_NORMAL;
+ /*
+ * 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, true) %
+ rbtdb->node_lock_count;
+#endif
+ /*
+ * Add an apex node to the NSEC3 tree so that NSEC3 searches
+ * return partial matches when there is only a single NSEC3
+ * record in the tree.
+ */
+ rbtdb->nsec3_origin_node = NULL;
+ result = dns_rbt_addnode(rbtdb->nsec3, &rbtdb->common.origin,
+ &rbtdb->nsec3_origin_node);
+ if (result != ISC_R_SUCCESS) {
+ INSIST(result != ISC_R_EXISTS);
+ free_rbtdb(rbtdb, false, NULL);
+ return (result);
+ }
+ rbtdb->nsec3_origin_node->nsec = DNS_RBT_NSEC_NSEC3;
+ /*
+ * We need to give the nsec3 origin node the right locknum.
+ */
+ dns_name_init(&name, NULL);
+ dns_rbt_namefromnode(rbtdb->nsec3_origin_node, &name);
+#ifdef DNS_RBT_USEHASH
+ rbtdb->nsec3_origin_node->locknum =
+ rbtdb->nsec3_origin_node->hashval %
+ rbtdb->node_lock_count;
+#else
+ rbtdb->nsec3_origin_node->locknum =
+ dns_name_hash(&name, true) %
+ rbtdb->node_lock_count;
+#endif
+ }
+
+ /*
+ * Misc. Initialization.
+ */
+ result = isc_refcount_init(&rbtdb->references, 1);
+ if (result != ISC_R_SUCCESS) {
+ free_rbtdb(rbtdb, false, NULL);
+ return (result);
+ }
+ rbtdb->attributes = 0;
+ rbtdb->task = NULL;
+ rbtdb->rpzs = NULL;
+ rbtdb->load_rpzs = NULL;
+ rbtdb->rpz_num = DNS_RPZ_INVALID_NUM;
+
+ /*
+ * Version Initialization.
+ */
+ rbtdb->current_serial = 1;
+ rbtdb->least_serial = 1;
+ rbtdb->next_serial = 2;
+ rbtdb->current_version = allocate_version(mctx, 1, 1, false);
+ if (rbtdb->current_version == NULL) {
+ isc_refcount_decrement(&rbtdb->references, NULL);
+ free_rbtdb(rbtdb, false, NULL);
+ return (ISC_R_NOMEMORY);
+ }
+ rbtdb->current_version->rbtdb = rbtdb;
+ rbtdb->current_version->secure = dns_db_insecure;
+ rbtdb->current_version->havensec3 = 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));
+ result = isc_rwlock_init(&rbtdb->current_version->rwlock, 0, 0);
+ if (result != ISC_R_SUCCESS) {
+ isc_refcount_destroy(&rbtdb->current_version->references);
+ isc_mem_put(mctx, rbtdb->current_version,
+ sizeof(*rbtdb->current_version));
+ rbtdb->current_version = NULL;
+ isc_refcount_decrement(&rbtdb->references, NULL);
+ free_rbtdb(rbtdb, false, NULL);
+ return (result);
+ }
+
+ rbtdb->current_version->records = 0;
+ rbtdb->current_version->bytes = 0;
+ 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(hmctx, 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);
+ INSIST(!ISC_LINK_LINKED(target, link));
+ *target = *source;
+ ISC_LINK_INIT(target, link);
+
+ /*
+ * 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 = &slab_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 = &slab_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 = &slab_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 = &slab_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);
+}
+
+static void
+rdataset_settrust(dns_rdataset_t *rdataset, dns_trust_t trust) {
+ dns_rbtdb_t *rbtdb = rdataset->private1;
+ dns_rbtnode_t *rbtnode = rdataset->private2;
+ rdatasetheader_t *header = rdataset->private3;
+
+ header--;
+ NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
+ isc_rwlocktype_write);
+ header->trust = rdataset->trust = trust;
+ NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
+ isc_rwlocktype_write);
+}
+
+static void
+rdataset_expire(dns_rdataset_t *rdataset) {
+ dns_rbtdb_t *rbtdb = rdataset->private1;
+ dns_rbtnode_t *rbtnode = rdataset->private2;
+ rdatasetheader_t *header = rdataset->private3;
+
+ header--;
+ NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
+ isc_rwlocktype_write);
+ expire_header(rbtdb, header, false, expire_flush);
+ NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
+ isc_rwlocktype_write);
+}
+
+static void
+rdataset_clearprefetch(dns_rdataset_t *rdataset) {
+ dns_rbtdb_t *rbtdb = rdataset->private1;
+ dns_rbtnode_t *rbtnode = rdataset->private2;
+ rdatasetheader_t *header = rdataset->private3;
+
+ header--;
+ NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
+ isc_rwlocktype_write);
+ header->attributes &= ~RDATASET_ATTR_PREFETCH;
+ NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
+ isc_rwlocktype_write);
+}
+
+/*
+ * 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, 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 (NEGATIVE(header)) {
+ 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 (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 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, false);
+ 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;
+ bool was_read_locked = false;
+ nodelock_t *lock;
+ int i;
+
+ if (rbtdbiter->delcnt != 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->delcnt,
+ dns_rbt_nodecount(rbtdb->tree));
+
+ if (rbtdbiter->tree_locked == isc_rwlocktype_read) {
+ RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+ was_read_locked = true;
+ }
+ RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
+ rbtdbiter->tree_locked = isc_rwlocktype_write;
+
+ for (i = 0; i < rbtdbiter->delcnt; 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, false);
+ NODE_UNLOCK(lock, isc_rwlocktype_read);
+ }
+
+ rbtdbiter->delcnt = 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 = 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_NOTFOUND &&
+ rbtdbiter->result != DNS_R_PARTIALMATCH &&
+ 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 = true;
+ reference_iter_node(rbtdbiter);
+ }
+ } else {
+ INSIST(result == ISC_R_NOTFOUND);
+ result = ISC_R_NOMORE; /* The tree is empty. */
+ }
+
+ rbtdbiter->result = result;
+
+ if (result != ISC_R_SUCCESS)
+ ENSURE(!rbtdbiter->paused);
+
+ 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_NOTFOUND &&
+ rbtdbiter->result != DNS_R_PARTIALMATCH &&
+ 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 = 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, tresult;
+ 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 != DNS_R_PARTIALMATCH &&
+ 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;
+ tresult = dns_rbt_findnode(rbtdb->nsec3, name, NULL,
+ &node, &rbtdbiter->nsec3chain,
+ DNS_RBTFIND_EMPTYDATA,
+ NULL, NULL);
+ if (tresult == ISC_R_SUCCESS) {
+ rbtdbiter->node = node;
+ rbtdbiter->current = &rbtdbiter->nsec3chain;
+ result = tresult;
+ }
+ }
+ }
+
+ if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
+ tresult = dns_rbtnodechain_current(rbtdbiter->current, iname,
+ origin, NULL);
+ if (tresult == ISC_R_SUCCESS) {
+ rbtdbiter->new_origin = 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;
+
+ 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 = (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 = (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->delcnt == 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->delcnt++] = 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_NOTFOUND &&
+ rbtdbiter->result != DNS_R_PARTIALMATCH &&
+ rbtdbiter->result != ISC_R_NOMORE)
+ return (rbtdbiter->result);
+
+ if (rbtdbiter->paused)
+ return (ISC_R_SUCCESS);
+
+ rbtdbiter->paused = 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);
+ acarray[count].cbarg = NULL;
+ 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;
+
+ if (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);
+}
+
+static void
+setownercase(rdatasetheader_t *header, const dns_name_t *name) {
+ unsigned int i;
+ bool fully_lower;
+
+ /*
+ * We do not need to worry about label lengths as they are all
+ * less than or equal to 63.
+ */
+ memset(header->upper, 0, sizeof(header->upper));
+ fully_lower = true;
+ for (i = 0; i < name->length; i++)
+ if (name->ndata[i] >= 0x41 && name->ndata[i] <= 0x5a) {
+ header->upper[i/8] |= 1 << (i%8);
+ fully_lower = false;
+ }
+ header->attributes |= RDATASET_ATTR_CASESET;
+ if (ISC_LIKELY(fully_lower))
+ header->attributes |= RDATASET_ATTR_CASEFULLYLOWER;
+}
+
+static void
+rdataset_setownercase(dns_rdataset_t *rdataset, const dns_name_t *name) {
+ unsigned char *raw = rdataset->private3; /* RDATASLAB */
+ rdatasetheader_t *header;
+
+ header = (struct rdatasetheader *)(raw - sizeof(*header));
+ setownercase(header, name);
+}
+
+static const unsigned char charmask[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+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
+};
+
+static void
+rdataset_getownercase(const dns_rdataset_t *rdataset, dns_name_t *name) {
+ const unsigned char *raw = rdataset->private3; /* RDATASLAB */
+ const rdatasetheader_t *header;
+ unsigned int i, j;
+ unsigned char bits;
+ unsigned char c, flip;
+
+ header = (const struct rdatasetheader *)(raw - sizeof(*header));
+
+ if (!CASESET(header))
+ return;
+
+#if 0
+ /*
+ * This was the original code, and is implemented differently in
+ * the #else block that follows.
+ */
+ for (i = 0; i < name->length; i++) {
+ /*
+ * Set the case bit if it does not match the recorded bit.
+ */
+ if (name->ndata[i] >= 0x61 && name->ndata[i] <= 0x7a &&
+ (header->upper[i/8] & (1 << (i%8))) != 0)
+ name->ndata[i] &= ~0x20; /* clear the lower case bit */
+ else if (name->ndata[i] >= 0x41 && name->ndata[i] <= 0x5a &&
+ (header->upper[i/8] & (1 << (i%8))) == 0)
+ name->ndata[i] |= 0x20; /* set the lower case bit */
+ }
+#else
+ if (ISC_LIKELY(CASEFULLYLOWER(header))) {
+ unsigned char *bp, *be;
+ bp = name->ndata;
+ be = bp + name->length;
+
+ while (bp <= be - 4) {
+ c = bp[0];
+ bp[0] = maptolower[c];
+ c = bp[1];
+ bp[1] = maptolower[c];
+ c = bp[2];
+ bp[2] = maptolower[c];
+ c = bp[3];
+ bp[3] = maptolower[c];
+ bp += 4;
+ }
+ while (bp < be) {
+ c = *bp;
+ *bp++ = maptolower[c];
+ }
+ return;
+ }
+
+ i = 0;
+ for (j = 0; j < (name->length >> 3); j++) {
+ unsigned int k;
+
+ bits = ~(header->upper[j]);
+
+ for (k = 0; k < 8; k++) {
+ c = name->ndata[i];
+ flip = (bits & 1) << 5;
+ flip ^= c;
+ flip &= charmask[c];
+ name->ndata[i] ^= flip;
+
+ i++;
+ bits >>= 1;
+ }
+ }
+
+ if (ISC_UNLIKELY(i == name->length))
+ return;
+
+ bits = ~(header->upper[j]);
+
+ for (; i < name->length; i++) {
+ c = name->ndata[i];
+ flip = (bits & 1) << 5;
+ flip ^= c;
+ flip &= charmask[c];
+ name->ndata[i] ^= flip;
+
+ bits >>= 1;
+ }
+#endif
+}
+
+/*%
+ * Routines for LRU-based cache management.
+ */
+
+/*%
+ * See if a given cache entry that is being reused needs to be updated
+ * in the LRU-list. From the LRU management point of view, this function is
+ * expected to return true for almost all cases. When used with threads,
+ * however, this may cause a non-negligible performance penalty because a
+ * writer lock will have to be acquired before updating the list.
+ * If DNS_RBTDB_LIMITLRUUPDATE is defined to be non 0 at compilation time, 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 bool
+need_headerupdate(rdatasetheader_t *header, isc_stdtime_t now) {
+ if ((header->attributes &
+ (RDATASET_ATTR_NONEXISTENT |
+ RDATASET_ATTR_STALE |
+ RDATASET_ATTR_ZEROTTL)) != 0)
+ return (false);
+
+#if DNS_RBTDB_LIMITLRUUPDATE
+ 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 (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, link));
+
+ ISC_LIST_UNLINK(rbtdb->rdatasets[header->node->locknum], header, link);
+ header->last_used = now;
+ ISC_LIST_PREPEND(rbtdb->rdatasets[header->node->locknum], header, 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, bool 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,
+ expire_ttl);
+ purgecount--;
+ }
+
+ for (header = ISC_LIST_TAIL(rbtdb->rdatasets[locknum]);
+ header != NULL && purgecount > 0;
+ header = header_prev) {
+ header_prev = ISC_LIST_PREV(header, link);
+ /*
+ * Unlink the entry at this point to avoid checking it
+ * again even if it's currently used someone else and
+ * cannot be purged at this moment. This entry won't be
+ * referenced any more (so unlinking is safe) since the
+ * TTL was reset to 0.
+ */
+ ISC_LIST_UNLINK(rbtdb->rdatasets[locknum], header,
+ link);
+ expire_header(rbtdb, header, tree_locked,
+ expire_lru);
+ purgecount--;
+ }
+
+ NODE_UNLOCK(&rbtdb->node_locks[locknum].lock,
+ isc_rwlocktype_write);
+ }
+}
+
+static void
+expire_header(dns_rbtdb_t *rbtdb, rdatasetheader_t *header,
+ bool tree_locked, expire_t reason)
+{
+ set_ttl(rbtdb, header, 0);
+ mark_stale_header(rbtdb, header);
+
+ /*
+ * 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, false);
+
+ if (rbtdb->cachestats == NULL)
+ return;
+
+ switch (reason) {
+ case expire_ttl:
+ isc_stats_increment(rbtdb->cachestats,
+ dns_cachestatscounter_deletettl);
+ break;
+ case expire_lru:
+ isc_stats_increment(rbtdb->cachestats,
+ dns_cachestatscounter_deletelru);
+ break;
+ default:
+ break;
+ }
+
+ }
+}
diff --git a/lib/dns/rbtdb.h b/lib/dns/rbtdb.h
new file mode 100644
index 0000000..a7cb42b
--- /dev/null
+++ b/lib/dns/rbtdb.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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);
+
+/*%<
+ * Create a new database of type "rbt" (or "rbt64"). Called via
+ * dns_db_create(); see documentation for that function for more details.
+ *
+ * If argv[0] is set, it points to a valid memory context to be used for
+ * allocation of heap memory. Generally this is used for cache databases
+ * only.
+ *
+ * Requires:
+ *
+ * \li argc == 0 or argv[0] is a valid memory context.
+ */
+
+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..6f4a659
--- /dev/null
+++ b/lib/dns/rbtdb64.c
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $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..faf85e5
--- /dev/null
+++ b/lib/dns/rbtdb64.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $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..6a5948e
--- /dev/null
+++ b/lib/dns/rcode.c
@@ -0,0 +1,593 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#include <config.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <stdbool.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 <pk11/site.h>
+
+#include <dns/cert.h>
+#include <dns/ds.h>
+#include <dns/dsdigest.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 TOTEXTONLY 0x01
+
+#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}, \
+ { 11, "RESERVED11", TOTEXTONLY}, \
+ { 12, "RESERVED12", TOTEXTONLY}, \
+ { 13, "RESERVED13", TOTEXTONLY}, \
+ { 14, "RESERVED14", TOTEXTONLY}, \
+ { 15, "RESERVED15", TOTEXTONLY},
+
+#define ERCODENAMES \
+ /* extended rcodes */ \
+ { dns_rcode_badvers, "BADVERS", 0}, \
+ { dns_rcode_badcookie, "BADCOOKIE", 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 }
+
+/* RFC4398 section 2.1 */
+
+#define CERTNAMES \
+ { 1, "PKIX", 0}, \
+ { 2, "SPKI", 0}, \
+ { 3, "PGP", 0}, \
+ { 4, "IPKIX", 0}, \
+ { 5, "ISPKI", 0}, \
+ { 6, "IPGP", 0}, \
+ { 7, "ACPKIX", 0}, \
+ { 8, "IACPKIX", 0}, \
+ { 253, "URI", 0}, \
+ { 254, "OID", 0}, \
+ { 0, NULL, 0}
+
+/* RFC2535 section 7, RFC3110 */
+
+#ifndef PK11_MD5_DISABLE
+#define MD5_SECALGNAMES \
+ { DNS_KEYALG_RSAMD5, "RSAMD5", 0 }, \
+ { DNS_KEYALG_RSAMD5, "RSA", 0 },
+#else
+#define MD5_SECALGNAMES
+#endif
+#ifndef PK11_DH_DISABLE
+#define DH_SECALGNAMES \
+ { DNS_KEYALG_DH, "DH", 0 },
+#else
+#define DH_SECALGNAMES
+#endif
+#ifndef PK11_DSA_DISABLE
+#define DSA_SECALGNAMES \
+ { DNS_KEYALG_DSA, "DSA", 0 }, \
+ { DNS_KEYALG_NSEC3DSA, "NSEC3DSA", 0 },
+#else
+#define DSA_SECALGNAMES
+#endif
+
+#define SECALGNAMES \
+ MD5_SECALGNAMES \
+ DH_SECALGNAMES \
+ DSA_SECALGNAMES \
+ { DNS_KEYALG_ECC, "ECC", 0 }, \
+ { DNS_KEYALG_RSASHA1, "RSASHA1", 0 }, \
+ { DNS_KEYALG_NSEC3RSASHA1, "NSEC3RSASHA1", 0 }, \
+ { DNS_KEYALG_RSASHA256, "RSASHA256", 0 }, \
+ { DNS_KEYALG_RSASHA512, "RSASHA512", 0 }, \
+ { DNS_KEYALG_ECCGOST, "ECCGOST", 0 }, \
+ { DNS_KEYALG_ECDSA256, "ECDSAP256SHA256", 0 }, \
+ { DNS_KEYALG_ECDSA384, "ECDSAP384SHA384", 0 }, \
+ { DNS_KEYALG_ED25519, "ED25519", 0 }, \
+ { DNS_KEYALG_ED448, "ED448", 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 }
+
+/* RFC3658, RFC4509, RFC5933, RFC6605 */
+
+#define DSDIGESTNAMES \
+ { DNS_DSDIGEST_SHA1, "SHA-1", 0 }, \
+ { DNS_DSDIGEST_SHA256, "SHA-256", 0 }, \
+ { DNS_DSDIGEST_GOST, "GOST", 0 }, \
+ { DNS_DSDIGEST_SHA384, "SHA-384", 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 tbl dsdigests[] = { DSDIGESTNAMES };
+
+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);
+
+ memmove(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, bool hex_allowed)
+{
+ isc_result_t result;
+ 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.
+ */
+ snprintf(buffer, sizeof(buffer), "%.*s",
+ (int)source->length, source->base);
+
+ 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, 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 &&
+ (table[i].flags & TOTEXTONLY) == 0 &&
+ 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));
+}
+
+void
+dns_secalg_format(dns_secalg_t alg, char *cp, unsigned int size) {
+ isc_buffer_t b;
+ isc_region_t r;
+ isc_result_t result;
+
+ REQUIRE(cp != NULL && size > 0);
+ isc_buffer_init(&b, cp, size - 1);
+ result = dns_secalg_totext(alg, &b);
+ isc_buffer_usedregion(&b, &r);
+ r.base[r.length] = 0;
+ if (result != ISC_R_SUCCESS)
+ r.base[0] = 0;
+}
+
+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, 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 = (unsigned int)(delim - text);
+ else
+ len = (unsigned int)(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);
+}
+
+isc_result_t
+dns_dsdigest_fromtext(dns_dsdigest_t *dsdigestp, isc_textregion_t *source) {
+ unsigned int value;
+ RETERR(dns_mnemonic_fromtext(&value, source, dsdigests, 0xff));
+ *dsdigestp = value;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_dsdigest_totext(dns_dsdigest_t dsdigest, isc_buffer_t *target) {
+ return (dns_mnemonic_totext(dsdigest, target, dsdigests));
+}
+
+void
+dns_dsdigest_format(dns_dsdigest_t typ, char *cp, unsigned int size) {
+ isc_buffer_t b;
+ isc_region_t r;
+ isc_result_t result;
+
+ REQUIRE(cp != NULL && size > 0);
+ isc_buffer_init(&b, cp, size - 1);
+ result = dns_dsdigest_totext(typ, &b);
+ isc_buffer_usedregion(&b, &r);
+ r.base[r.length] = 0;
+ if (result != ISC_R_SUCCESS)
+ r.base[0] = 0;
+}
+
+/*
+ * 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;
+
+ /*
+ * source->base is not required to be NUL terminated.
+ * Copy up to remaining bytes and NUL terminate.
+ */
+ snprintf(buf, sizeof(buf), "%.*s",
+ (int)(source->length - 5), source->base + 5);
+ 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) {
+ 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:
+ return (dns_rdataclass_tounknowntext(rdclass, target));
+ }
+}
+
+isc_result_t
+dns_rdataclass_tounknowntext(dns_rdataclass_t rdclass, isc_buffer_t *target) {
+ char buf[sizeof("CLASS65535")];
+
+ 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;
+
+ if (size == 0U)
+ return;
+
+ 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)
+ strlcpy(array, "<unknown>", size);
+}
diff --git a/lib/dns/rdata.c b/lib/dns/rdata.c
new file mode 100644
index 0000000..93f5559
--- /dev/null
+++ b/lib/dns/rdata.c
@@ -0,0 +1,2432 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <ctype.h>
+#include <inttypes.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/dsdigest.h>
+#include <dns/enumtype.h>
+#include <dns/keyflags.h>
+#include <dns/keyvalues.h>
+#include <dns/message.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 CHECK(op) \
+ do { result = (op); \
+ if (result != ISC_R_SUCCESS) goto cleanup; \
+ } while (0)
+
+#define CHECKTOK(op) \
+ do { result = (op); \
+ if (result != ISC_R_SUCCESS) { \
+ isc_lex_ungettoken(lexer, &token); \
+ goto cleanup; \
+ } \
+ } 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 const 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, bool 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, bool quote, 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_result_t
+multitxt_totext(isc_region_t *source, isc_buffer_t *target);
+
+static isc_result_t
+multitxt_fromtext(isc_textregion_t *source, isc_buffer_t *target);
+
+static bool
+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 bool
+buffer_empty(isc_buffer_t *source);
+
+static void
+buffer_fromregion(isc_buffer_t *buffer, isc_region_t *region);
+
+static isc_result_t
+uint32_tobuffer(uint32_t, isc_buffer_t *target);
+
+static isc_result_t
+uint16_tobuffer(uint32_t, isc_buffer_t *target);
+
+static isc_result_t
+uint8_tobuffer(uint32_t, isc_buffer_t *target);
+
+static isc_result_t
+name_tobuffer(dns_name_t *name, isc_buffer_t *target);
+
+static uint32_t
+uint32_fromregion(isc_region_t *region);
+
+static uint16_t
+uint16_fromregion(isc_region_t *region);
+
+static uint8_t
+uint8_fromregion(isc_region_t *region);
+
+static 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 uint16_t
+uint16_consume_fromregion(isc_region_t *region);
+
+static isc_result_t
+unknown_totext(dns_rdata_t *rdata, dns_rdata_textctx_t *tctx,
+ isc_buffer_t *target);
+
+static inline isc_result_t
+generic_fromtext_key(ARGS_FROMTEXT);
+
+static inline isc_result_t
+generic_totext_key(ARGS_TOTEXT);
+
+static inline isc_result_t
+generic_fromwire_key(ARGS_FROMWIRE);
+
+static inline isc_result_t
+generic_fromstruct_key(ARGS_FROMSTRUCT);
+
+static inline isc_result_t
+generic_tostruct_key(ARGS_TOSTRUCT);
+
+static inline void
+generic_freestruct_key(ARGS_FREESTRUCT);
+
+static isc_result_t
+generic_fromtext_txt(ARGS_FROMTEXT);
+
+static isc_result_t
+generic_totext_txt(ARGS_TOTEXT);
+
+static isc_result_t
+generic_fromwire_txt(ARGS_FROMWIRE);
+
+static isc_result_t
+generic_fromstruct_txt(ARGS_FROMSTRUCT);
+
+static isc_result_t
+generic_tostruct_txt(ARGS_TOSTRUCT);
+
+static void
+generic_freestruct_txt(ARGS_FREESTRUCT);
+
+static isc_result_t
+generic_txt_first(dns_rdata_txt_t *txt);
+
+static isc_result_t
+generic_txt_next(dns_rdata_txt_t *txt);
+
+static isc_result_t
+generic_txt_current(dns_rdata_txt_t *txt, dns_rdata_txt_string_t *string);
+
+static isc_result_t
+generic_totext_ds(ARGS_TOTEXT);
+
+static isc_result_t
+generic_tostruct_ds(ARGS_TOSTRUCT);
+
+static isc_result_t
+generic_fromtext_ds(ARGS_FROMTEXT);
+
+static isc_result_t
+generic_fromwire_ds(ARGS_FROMWIRE);
+
+static isc_result_t
+generic_fromstruct_ds(ARGS_FROMSTRUCT);
+
+static isc_result_t
+generic_fromtext_tlsa(ARGS_FROMTEXT);
+
+static isc_result_t
+generic_totext_tlsa(ARGS_TOTEXT);
+
+static isc_result_t
+generic_fromwire_tlsa(ARGS_FROMWIRE);
+
+static isc_result_t
+generic_fromstruct_tlsa(ARGS_FROMSTRUCT);
+
+static isc_result_t
+generic_tostruct_tlsa(ARGS_TOSTRUCT);
+
+static void
+generic_freestruct_tlsa(ARGS_FREESTRUCT);
+
+/*% INT16 Size */
+#define NS_INT16SZ 2
+/*% IPv6 Address Size */
+#define NS_LOCATORSZ 8
+
+/*
+ * Active Diretory gc._msdcs.<forest> prefix.
+ */
+static unsigned char gc_msdcs_data[] = "\002gc\006_msdcs";
+static unsigned char gc_msdcs_offset [] = { 0, 3 };
+
+static dns_name_t const gc_msdcs =
+ DNS_NAME_INITNONABSOLUTE(gc_msdcs_data, gc_msdcs_offset);
+
+/*%
+ * convert presentation level address to network order binary form.
+ * \return
+ * 1 if `src' is a valid [RFC1884 2.2] address, else 0.
+ * \note
+ * (1) does not touch `dst' unless it's returning 1.
+ */
+static inline int
+locator_pton(const char *src, unsigned char *dst) {
+ static const char xdigits_l[] = "0123456789abcdef",
+ xdigits_u[] = "0123456789ABCDEF";
+ unsigned char tmp[NS_LOCATORSZ];
+ unsigned char *tp = tmp, *endp;
+ const char *xdigits;
+ int ch, seen_xdigits;
+ unsigned int val;
+
+ memset(tp, '\0', NS_LOCATORSZ);
+ endp = tp + NS_LOCATORSZ;
+ seen_xdigits = 0;
+ val = 0;
+ while ((ch = *src++) != '\0') {
+ const char *pch;
+
+ pch = strchr((xdigits = xdigits_l), ch);
+ if (pch == NULL)
+ pch = strchr((xdigits = xdigits_u), ch);
+ if (pch != NULL) {
+ val <<= 4;
+ val |= (pch - xdigits);
+ if (++seen_xdigits > 4)
+ return (0);
+ continue;
+ }
+ if (ch == ':') {
+ if (!seen_xdigits)
+ return (0);
+ if (tp + NS_INT16SZ > endp)
+ return (0);
+ *tp++ = (unsigned char) (val >> 8) & 0xff;
+ *tp++ = (unsigned char) val & 0xff;
+ seen_xdigits = 0;
+ val = 0;
+ continue;
+ }
+ return (0);
+ }
+ if (seen_xdigits) {
+ if (tp + NS_INT16SZ > endp)
+ return (0);
+ *tp++ = (unsigned char) (val >> 8) & 0xff;
+ *tp++ = (unsigned char) val & 0xff;
+ }
+ if (tp != endp)
+ return (0);
+ memmove(dst, tmp, NS_LOCATORSZ);
+ return (1);
+}
+
+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 *copy;
+
+ if (mctx == NULL)
+ return (source);
+ copy = isc_mem_allocate(mctx, length);
+ if (copy != NULL)
+ memmove(copy, source, length);
+
+ return (copy);
+}
+
+static inline isc_result_t
+typemap_fromtext(isc_lex_t *lexer, isc_buffer_t *target,
+ bool allow_empty)
+{
+ isc_token_t token;
+ unsigned char bm[8*1024]; /* 64k bits */
+ dns_rdatatype_t covered, max_used;
+ int octet;
+ unsigned int max_octet, newend, end;
+ int window;
+ bool first = true;
+
+ max_used = 0;
+ bm[0] = 0;
+ end = 0;
+
+ do {
+ RETERR(isc_lex_getmastertoken(lexer, &token,
+ isc_tokentype_string, true));
+ if (token.type != isc_tokentype_string)
+ break;
+ RETTOK(dns_rdatatype_fromtext(&covered,
+ &token.value.as_textregion));
+ if (covered > max_used) {
+ newend = covered / 8;
+ if (newend > end) {
+ memset(&bm[end + 1], 0, newend - end);
+ end = newend;
+ }
+ max_used = covered;
+ }
+ bm[covered/8] |= (0x80>>(covered%8));
+ first = false;
+ } while (1);
+ isc_lex_ungettoken(lexer, &token);
+ if (!allow_empty && first)
+ return (DNS_R_FORMERR);
+
+ for (window = 0; window < 256 ; window++) {
+ if (max_used < window * 256)
+ break;
+
+ max_octet = max_used - (window * 256);
+ if (max_octet >= 256)
+ max_octet = 31;
+ else
+ max_octet /= 8;
+
+ /*
+ * Find if we have a type in this window.
+ */
+ for (octet = max_octet; 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
+typemap_totext(isc_region_t *sr, dns_rdata_textctx_t *tctx,
+ isc_buffer_t *target)
+{
+ unsigned int i, j, k;
+ unsigned int window, len;
+ bool first = true;
+
+ for (i = 0; i < sr->length; i += len) {
+ if (tctx != NULL &&
+ (tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) {
+ RETERR(str_totext(tctx->linebreak, target));
+ first = true;
+ }
+ 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;
+ if (!first)
+ RETERR(str_totext(" ", target));
+ first = false;
+ if (dns_rdatatype_isknown(t)) {
+ RETERR(dns_rdatatype_totext(t, target));
+ } else {
+ char buf[sizeof("TYPE65535")];
+ snprintf(buf, sizeof(buf), "TYPE%u", t);
+ RETERR(str_totext(buf, target));
+ }
+ }
+ }
+ }
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+typemap_test(isc_region_t *sr, bool allow_empty) {
+ unsigned int window, lastwindow = 0;
+ unsigned int len;
+ bool first = true;
+ unsigned int i;
+
+ 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 = false;
+ }
+ if (i != sr->length)
+ return (DNS_R_EXTRADATA);
+ if (!allow_empty && first)
+ RETERR(DNS_R_FORMERR);
+ return (ISC_R_SUCCESS);
+}
+
+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); */
+}
+
+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;
+ bool use_default = false;
+
+ REQUIRE(rdata1 != NULL);
+ REQUIRE(rdata2 != NULL);
+ REQUIRE(rdata1->length == 0 || rdata1->data != NULL);
+ REQUIRE(rdata2->length == 0 || 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);
+}
+
+int
+dns_rdata_casecompare(const dns_rdata_t *rdata1, const dns_rdata_t *rdata2) {
+ int result = 0;
+ bool use_default = false;
+
+ REQUIRE(rdata1 != NULL);
+ REQUIRE(rdata2 != NULL);
+ REQUIRE(rdata1->length == 0 || rdata1->data != NULL);
+ REQUIRE(rdata2->length == 0 || 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);
+
+ CASECOMPARESWITCH
+
+ 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;
+ bool use_default = false;
+ uint32_t activelength;
+ unsigned int length;
+
+ REQUIRE(dctx != NULL);
+ if (rdata != NULL) {
+ REQUIRE(DNS_RDATA_INITIALIZED(rdata));
+ REQUIRE(DNS_RDATA_VALIDFLAGS(rdata));
+ }
+ REQUIRE(source != NULL);
+ REQUIRE(target != NULL);
+
+ 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;
+ }
+ }
+
+ /*
+ * Reject any rdata that expands out to more than DNS_RDATA_MAXLENGTH
+ * as we cannot transmit it.
+ */
+ length = isc_buffer_usedlength(target) - isc_buffer_usedlength(&st);
+ if (result == ISC_R_SUCCESS && length > DNS_RDATA_MAXLENGTH)
+ result = DNS_R_FORMERR;
+
+ /*
+ * 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 = length;
+ 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;
+ bool use_default = 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);
+ memmove(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, (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;
+ isc_result_t result;
+
+ dns_decompress_init(&dctx, -1, DNS_DECOMPRESS_NONE);
+ isc_buffer_setactive(src, isc_buffer_usedlength(src));
+ result = dns_rdata_fromwire(NULL, 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);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ false));
+ if (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;
+ unsigned int length;
+ bool unknown;
+
+ REQUIRE(origin == NULL || dns_name_isabsolute(origin) == 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,
+ false);
+ if (result != ISC_R_SUCCESS) {
+ name = isc_lex_getsourcename(lexer);
+ line = isc_lex_getsourceline(lexer);
+ fromtext_error(callback, callbacks, name, line, NULL, result);
+ return (result);
+ }
+
+ unknown = false;
+ if (token.type == isc_tokentype_string &&
+ strcmp(DNS_AS_STR(token), "\\#") == 0) {
+ /*
+ * If this is a TXT record '\#' could be a escaped '#'.
+ * Look to see if the next token is a number and if so
+ * treat it as a unknown record format.
+ */
+ if (type == dns_rdatatype_txt) {
+ result = isc_lex_getmastertoken(lexer, &token,
+ isc_tokentype_number,
+ false);
+ if (result == ISC_R_SUCCESS)
+ isc_lex_ungettoken(lexer, &token);
+ }
+
+ if (result == ISC_R_SUCCESS) {
+ unknown = true;
+ result = unknown_fromtext(rdclass, type, lexer,
+ mctx, target);
+ } else
+ options |= DNS_RDATA_UNKNOWNESCAPE;
+ } else
+ isc_lex_ungettoken(lexer, &token);
+
+ if (!unknown)
+ 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);
+
+ length = isc_buffer_usedlength(target) - isc_buffer_usedlength(&st);
+ if (result == ISC_R_SUCCESS && length > DNS_RDATA_MAXLENGTH)
+ result = ISC_R_NOSPACE;
+
+ if (rdata != NULL && result == ISC_R_SUCCESS) {
+ region.base = isc_buffer_used(&st);
+ region.length = length;
+ dns_rdata_fromregion(rdata, rdclass, type, &region);
+ }
+ if (result != ISC_R_SUCCESS) {
+ *target = st;
+ }
+ return (result);
+}
+
+static isc_result_t
+unknown_totext(dns_rdata_t *rdata, dns_rdata_textctx_t *tctx,
+ isc_buffer_t *target)
+{
+ isc_result_t result;
+ char buf[sizeof("65535")];
+ isc_region_t sr;
+
+ strlcpy(buf, "\\# ", sizeof(buf));
+ result = str_totext(buf, target);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ dns_rdata_toregion(rdata, &sr);
+ INSIST(sr.length < 65536);
+ snprintf(buf, sizeof(buf), "%u", sr.length);
+ result = str_totext(buf, target);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ if (sr.length != 0U) {
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ result = str_totext(" ( ", target);
+ else
+ result = str_totext(" ", target);
+
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ if (tctx->width == 0) /* No splitting */
+ result = isc_hex_totext(&sr, 0, "", target);
+ else
+ 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);
+}
+
+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;
+ bool use_default = false;
+ unsigned int cur;
+
+ REQUIRE(rdata != NULL);
+ REQUIRE(tctx->origin == NULL ||
+ dns_name_isabsolute(tctx->origin) == true);
+
+ /*
+ * Some DynDNS meta-RRs have empty rdata.
+ */
+ if ((rdata->flags & DNS_RDATA_UPDATE) != 0) {
+ INSIST(rdata->length == 0);
+ return (ISC_R_SUCCESS);
+ }
+
+ if ((tctx->flags & DNS_STYLEFLAG_UNKNOWNFORMAT) != 0)
+ return (unknown_totext(rdata, tctx, target));
+
+ cur = isc_buffer_usedlength(target);
+
+ TOTEXTSWITCH
+
+ if (use_default || (result == ISC_R_NOTIMPLEMENTED)) {
+ unsigned int u = isc_buffer_usedlength(target);
+
+ INSIST(u >= cur);
+ isc_buffer_subtract(target, u - cur);
+ result = unknown_totext(rdata, tctx, 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,
+ unsigned int split_width, const 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 (split_width == 0xffffffff)
+ tctx.width = width;
+ else
+ tctx.width = split_width;
+
+ if ((flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ tctx.linebreak = linebreak;
+ else {
+ if (split_width == 0xffffffff)
+ 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;
+ bool use_default = false;
+ unsigned int length;
+
+ REQUIRE(source != NULL);
+ if (rdata != NULL) {
+ REQUIRE(DNS_RDATA_INITIALIZED(rdata));
+ REQUIRE(DNS_RDATA_VALIDFLAGS(rdata));
+ }
+
+ st = *target;
+
+ FROMSTRUCTSWITCH
+
+ if (use_default)
+ (void)NULL;
+
+ length = isc_buffer_usedlength(target) - isc_buffer_usedlength(&st);
+ if (result == ISC_R_SUCCESS && length > DNS_RDATA_MAXLENGTH)
+ result = ISC_R_NOSPACE;
+
+ if (rdata != NULL && result == ISC_R_SUCCESS) {
+ region.base = isc_buffer_used(&st);
+ region.length = length;
+ dns_rdata_fromregion(rdata, rdclass, type, &region);
+ }
+ if (result != ISC_R_SUCCESS)
+ *target = st;
+ return (result);
+}
+
+isc_result_t
+dns_rdata_tostruct(const dns_rdata_t *rdata, void *target, isc_mem_t *mctx) {
+ isc_result_t result = ISC_R_NOTIMPLEMENTED;
+ bool use_default = 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;
+ bool use_default = 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;
+ bool use_default = 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);
+}
+
+bool
+dns_rdata_checkowner(dns_name_t *name, dns_rdataclass_t rdclass,
+ dns_rdatatype_t type, bool wildcard)
+{
+ bool result;
+
+ CHECKOWNERSWITCH
+ return (result);
+}
+
+bool
+dns_rdata_checknames(dns_rdata_t *rdata, dns_name_t *owner, dns_name_t *bad)
+{
+ bool 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;
+
+ /*
+ * source->base is not required to be NUL terminated.
+ * Copy up to remaining bytes and NUL terminate.
+ */
+ snprintf(buf, sizeof(buf), "%.*s",
+ (int)(source->length - 4), source->base + 4);
+ 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) {
+ RDATATYPE_TOTEXT_SW
+
+ return (dns_rdatatype_tounknowntext(type, target));
+}
+
+isc_result_t
+dns_rdatatype_tounknowntext(dns_rdatatype_t type, isc_buffer_t *target) {
+ char buf[sizeof("TYPE65535")];
+
+ 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;
+
+ if (size == 0U)
+ return;
+
+ 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)
+ strlcpy(array, "<unknown>", size);
+}
+
+/*
+ * Private function.
+ */
+
+static unsigned int
+name_length(dns_name_t *name) {
+ return (name->length);
+}
+
+static isc_result_t
+txt_totext(isc_region_t *source, bool quote, 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 (n == 0U)
+ REQUIRE(quote == true);
+
+ if (quote) {
+ if (tl < 1)
+ return (ISC_R_NOSPACE);
+ *tp++ = '"';
+ tl--;
+ }
+ while (n--) {
+ /*
+ * \DDD space (0x20) if not quoting.
+ */
+ if (*sp < (quote ? 0x20 : 0x21) || *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;
+ }
+ /*
+ * Escape double quote and backslash. If we are not
+ * enclosing the string in double quotes also escape
+ * at sign and semicolon.
+ */
+ if (*sp == 0x22 || *sp == 0x5c ||
+ (!quote && (*sp == 0x40 || *sp == 0x3b))) {
+ if (tl < 2)
+ return (ISC_R_NOSPACE);
+ *tp++ = '\\';
+ tl--;
+ }
+ if (tl < 1)
+ return (ISC_R_NOSPACE);
+ *tp++ = *sp++;
+ tl--;
+ }
+ if (quote) {
+ if (tl < 1)
+ return (ISC_R_NOSPACE);
+ *tp++ = '"';
+ tl--;
+ POST(tl);
+ }
+ isc_buffer_add(target, (unsigned int)(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;
+ bool 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 = 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 = true;
+ continue;
+ }
+ escape = false;
+ if (nrem == 0)
+ return ((tregion.length <= 256U) ?
+ ISC_R_NOSPACE : DNS_R_SYNTAX);
+ *t++ = c;
+ nrem--;
+ }
+ if (escape)
+ return (DNS_R_SYNTAX);
+ *tregion.base = (unsigned char)(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);
+
+ if (tregion.base != sregion.base)
+ memmove(tregion.base, sregion.base, n);
+ isc_buffer_forward(source, n);
+ isc_buffer_add(target, n);
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Conversion of TXT-like rdata fields without length limits.
+ */
+static isc_result_t
+multitxt_totext(isc_region_t *source, isc_buffer_t *target) {
+ unsigned int tl;
+ unsigned int n0, 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;
+
+ if (tl < 1)
+ return (ISC_R_NOSPACE);
+ *tp++ = '"';
+ tl--;
+ do {
+ n = source->length;
+ n0 = source->length - 1;
+
+ 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, backslash */
+ if (*sp == 0x22 || *sp == 0x5c) {
+ if (tl < 2)
+ return (ISC_R_NOSPACE);
+ *tp++ = '\\';
+ tl--;
+ }
+ if (tl < 1)
+ return (ISC_R_NOSPACE);
+ *tp++ = *sp++;
+ tl--;
+ }
+ isc_region_consume(source, n0 + 1);
+ } while (source->length != 0);
+ if (tl < 1)
+ return (ISC_R_NOSPACE);
+ *tp++ = '"';
+ tl--;
+ POST(tl);
+ isc_buffer_add(target, (unsigned int)(tp - (char *)region.base));
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+multitxt_fromtext(isc_textregion_t *source, isc_buffer_t *target) {
+ isc_region_t tregion;
+ bool escape;
+ unsigned int n, nrem;
+ char *s;
+ unsigned char *t0, *t;
+ int d;
+ int c;
+
+ s = source->base;
+ n = source->length;
+ escape = false;
+
+ do {
+ isc_buffer_availableregion(target, &tregion);
+ t0 = t = tregion.base;
+ nrem = tregion.length;
+ if (nrem < 1)
+ return (ISC_R_NOSPACE);
+
+ while (n != 0) {
+ --n;
+ 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 = true;
+ continue;
+ }
+ escape = false;
+ *t++ = c;
+ nrem--;
+ if (nrem == 0)
+ break;
+ }
+ if (escape)
+ return (DNS_R_SYNTAX);
+
+ isc_buffer_add(target, (unsigned int)(t - t0));
+ } while (n != 0);
+ return (ISC_R_SUCCESS);
+}
+
+static bool
+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;
+
+ /* Master files should be case preserving. */
+ dns_name_getlabelsequence(name, l1 - l2, l2, target);
+ if (!dns_name_caseequal(origin, target))
+ goto return_false;
+
+ dns_name_getlabelsequence(name, 0, l1 - l2, target);
+ return (true);
+
+return_false:
+ *target = *name;
+ return (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);
+
+ memmove(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 bool
+buffer_empty(isc_buffer_t *source) {
+ return((source->current == source->active) ? true : 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(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(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, (uint16_t)value);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+uint8_tobuffer(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, (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 uint32_t
+uint32_fromregion(isc_region_t *region) {
+ uint32_t value;
+
+ REQUIRE(region->length >= 4);
+ value = (uint32_t)region->base[0] << 24;
+ value |= (uint32_t)region->base[1] << 16;
+ value |= (uint32_t)region->base[2] << 8;
+ value |= (uint32_t)region->base[3];
+ return(value);
+}
+
+static uint16_t
+uint16_consume_fromregion(isc_region_t *region) {
+ uint16_t r = uint16_fromregion(region);
+
+ isc_region_consume(region, 2);
+ return r;
+}
+
+static uint16_t
+uint16_fromregion(isc_region_t *region) {
+
+ REQUIRE(region->length >= 2);
+
+ return ((region->base[0] << 8) | region->base[1]);
+}
+
+static uint8_t
+uint8_fromregion(isc_region_t *region) {
+
+ REQUIRE(region->length >= 1);
+
+ return (region->base[0]);
+}
+
+static uint8_t
+uint8_consume_fromregion(isc_region_t *region) {
+ 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;
+
+ if (length == 0U)
+ return (ISC_R_SUCCESS);
+
+ isc_buffer_availableregion(target, &tr);
+ if (length > tr.length)
+ return (ISC_R_NOSPACE);
+ if (tr.base != base)
+ memmove(tr.base, base, length);
+ isc_buffer_add(target, length);
+ return (ISC_R_SUCCESS);
+}
+
+static int
+hexvalue(char value) {
+ const 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 (int)(s - hexdigits);
+}
+
+static int
+decvalue(char value) {
+ const 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 (int)(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 {
+ int32_t Ceor;
+ int32_t Csum;
+ int32_t Crot;
+ int32_t word;
+ 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) {
+ const 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 = (int32_t)(s - atob_digits);
+ ++bcount;
+ } else if (bcount < 4) {
+ word = times85(word);
+ word += (int32_t)(s - atob_digits);
+ ++bcount;
+ } else {
+ word = times85(word);
+ word += (int32_t)(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,
+ 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,
+ false));
+ if ((token.value.as_ulong % 4) != 0U) {
+ unsigned long padding = 4 - (token.value.as_ulong % 4);
+ if (isc_buffer_usedlength(target) < padding)
+ return (DNS_R_SYNTAX);
+ isc_buffer_subtract(target, padding);
+ }
+
+ /*
+ * Checksum.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ 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,
+ 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,
+ 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 int32_t tmpword = word;
+
+ if (tmpword < 0) {
+ /*
+ * Because some don't support u_long.
+ */
+ tmp = 32;
+ tmpword -= (int32_t)(85 * 85 * 85 * 85 * 32);
+ }
+ if (tmpword < 0) {
+ tmp = 64;
+ tmpword -= (int32_t)(85 * 85 * 85 * 85 * 32);
+ }
+ if (tr.length < 5)
+ return (ISC_R_NOSPACE);
+ tr.base[0] = atob_digits[(tmpword /
+ (int32_t)(85 * 85 * 85 * 85))
+ + tmp];
+ tmpword %= (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 == dns_rdatatype_rrsig)
+ return (covers_rrsig(rdata));
+ return (covers_sig(rdata));
+}
+
+bool
+dns_rdatatype_ismeta(dns_rdatatype_t type) {
+ if ((dns_rdatatype_attributes(type) & DNS_RDATATYPEATTR_META) != 0)
+ return (true);
+ return (false);
+}
+
+bool
+dns_rdatatype_issingleton(dns_rdatatype_t type) {
+ if ((dns_rdatatype_attributes(type) & DNS_RDATATYPEATTR_SINGLETON)
+ != 0)
+ return (true);
+ return (false);
+}
+
+bool
+dns_rdatatype_notquestion(dns_rdatatype_t type) {
+ if ((dns_rdatatype_attributes(type) & DNS_RDATATYPEATTR_NOTQUESTION)
+ != 0)
+ return (true);
+ return (false);
+}
+
+bool
+dns_rdatatype_questiononly(dns_rdatatype_t type) {
+ if ((dns_rdatatype_attributes(type) & DNS_RDATATYPEATTR_QUESTIONONLY)
+ != 0)
+ return (true);
+ return (false);
+}
+
+bool
+dns_rdatatype_atparent(dns_rdatatype_t type) {
+ if ((dns_rdatatype_attributes(type) & DNS_RDATATYPEATTR_ATPARENT) != 0)
+ return (true);
+ return (false);
+}
+
+bool
+dns_rdataclass_ismeta(dns_rdataclass_t rdclass) {
+
+ if (rdclass == dns_rdataclass_reserved0
+ || rdclass == dns_rdataclass_none
+ || rdclass == dns_rdataclass_any)
+ return (true);
+
+ return (false); /* Assume it is not a meta class. */
+}
+
+bool
+dns_rdatatype_isdnssec(dns_rdatatype_t type) {
+ if ((dns_rdatatype_attributes(type) & DNS_RDATATYPEATTR_DNSSEC) != 0)
+ return (true);
+ return (false);
+}
+
+bool
+dns_rdatatype_iszonecutauth(dns_rdatatype_t type) {
+ if ((dns_rdatatype_attributes(type)
+ & (DNS_RDATATYPEATTR_DNSSEC | DNS_RDATATYPEATTR_ZONECUTAUTH))
+ != 0)
+ return (true);
+ return (false);
+}
+
+bool
+dns_rdatatype_isknown(dns_rdatatype_t type) {
+ if ((dns_rdatatype_attributes(type) & DNS_RDATATYPEATTR_UNKNOWN)
+ == 0)
+ return (true);
+ return (false);
+}
+
+void
+dns_rdata_exists(dns_rdata_t *rdata, dns_rdatatype_t type) {
+
+ REQUIRE(rdata != NULL);
+ REQUIRE(DNS_RDATA_INITIALIZED(rdata));
+
+ rdata->data = NULL;
+ rdata->length = 0;
+ rdata->flags = DNS_RDATA_UPDATE;
+ rdata->type = type;
+ rdata->rdclass = dns_rdataclass_any;
+}
+
+void
+dns_rdata_notexist(dns_rdata_t *rdata, dns_rdatatype_t type) {
+
+ REQUIRE(rdata != NULL);
+ REQUIRE(DNS_RDATA_INITIALIZED(rdata));
+
+ rdata->data = NULL;
+ rdata->length = 0;
+ rdata->flags = DNS_RDATA_UPDATE;
+ rdata->type = type;
+ rdata->rdclass = dns_rdataclass_none;
+}
+
+void
+dns_rdata_deleterrset(dns_rdata_t *rdata, dns_rdatatype_t type) {
+
+ REQUIRE(rdata != NULL);
+ REQUIRE(DNS_RDATA_INITIALIZED(rdata));
+
+ rdata->data = NULL;
+ rdata->length = 0;
+ rdata->flags = DNS_RDATA_UPDATE;
+ rdata->type = type;
+ rdata->rdclass = dns_rdataclass_any;
+}
+
+void
+dns_rdata_makedelete(dns_rdata_t *rdata) {
+ REQUIRE(rdata != NULL);
+
+ rdata->rdclass = dns_rdataclass_none;
+}
+
+const char *
+dns_rdata_updateop(dns_rdata_t *rdata, dns_section_t section) {
+
+ REQUIRE(rdata != NULL);
+ REQUIRE(DNS_RDATA_INITIALIZED(rdata));
+
+ switch (section) {
+ case DNS_SECTION_PREREQUISITE:
+ switch (rdata->rdclass) {
+ case dns_rdataclass_none:
+ switch (rdata->type) {
+ case dns_rdatatype_any:
+ return ("domain doesn't exist");
+ default:
+ return ("rrset doesn't exist");
+ }
+ case dns_rdataclass_any:
+ switch (rdata->type) {
+ case dns_rdatatype_any:
+ return ("domain exists");
+ default:
+ return ("rrset exists (value independent)");
+ }
+ default:
+ return ("rrset exists (value dependent)");
+ }
+ case DNS_SECTION_UPDATE:
+ switch (rdata->rdclass) {
+ case dns_rdataclass_none:
+ return ("delete");
+ case dns_rdataclass_any:
+ switch (rdata->type) {
+ case dns_rdatatype_any:
+ return ("delete all rrsets");
+ default:
+ return ("delete rrset");
+ }
+ default:
+ return ("add");
+ }
+ }
+ return ("invalid");
+}
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..5cb24b2
--- /dev/null
+++ b/lib/dns/rdata/any_255/tsig_250.c
@@ -0,0 +1,598 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#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;
+ uint64_t sigtime;
+ isc_buffer_t buffer;
+ dns_rcode_t rcode;
+ long i;
+ char *e;
+
+ REQUIRE(type == dns_rdatatype_tsig);
+ REQUIRE(rdclass == dns_rdataclass_any);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ /*
+ * Algorithm Name.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ false));
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ if (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,
+ 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((uint16_t)(sigtime >> 32), target));
+ RETERR(uint32_tobuffer((uint32_t)(sigtime & 0xffffffffU), target));
+
+ /*
+ * Fudge.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ 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,
+ 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,
+ 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,
+ 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,
+ 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;
+ bool sub;
+ uint64_t sigtime;
+ unsigned short n;
+
+ REQUIRE(rdata->type == dns_rdatatype_tsig);
+ REQUIRE(rdata->rdclass == dns_rdataclass_any);
+ 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 = ((uint64_t)sr.base[0] << 40) |
+ ((uint64_t)sr.base[1] << 32) |
+ ((uint64_t)sr.base[2] << 24) |
+ ((uint64_t)sr.base[3] << 16) |
+ ((uint64_t)sr.base[4] << 8) |
+ (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);
+ snprintf(buf, sizeof(buf), "%u ", n);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * Signature Size.
+ */
+ n = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+ snprintf(buf, sizeof(buf), "%u", n);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * Signature.
+ */
+ if (n != 0U) {
+ 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));
+ if (tctx->width == 0) /* No splitting */
+ RETERR(isc_base64_totext(&sigr, 60, "", target));
+ else
+ 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);
+ } else {
+ RETERR(str_totext(" ", target));
+ }
+
+ /*
+ * Original ID.
+ */
+ n = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+ snprintf(buf, sizeof(buf), "%u ", n);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * Error.
+ */
+ n = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+ RETERR(dns_tsigrcode_totext((dns_rcode_t)n, target));
+
+ /*
+ * Other Size.
+ */
+ n = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+ snprintf(buf, sizeof(buf), " %u ", n);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * Other.
+ */
+ if (tctx->width == 0) /* No splitting */
+ return (isc_base64_totext(&sr, 60, "", target));
+ else
+ 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 == dns_rdatatype_tsig);
+ REQUIRE(rdclass == dns_rdataclass_any);
+
+ 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 == dns_rdatatype_tsig);
+ REQUIRE(rdata->rdclass == dns_rdataclass_any);
+ 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 == dns_rdatatype_tsig);
+ REQUIRE(rdata1->rdclass == dns_rdataclass_any);
+ 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 == dns_rdatatype_tsig);
+ REQUIRE(rdclass == dns_rdataclass_any);
+ 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((uint16_t)(tsig->timesigned >> 32),
+ target));
+ RETERR(uint32_tobuffer((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 == dns_rdatatype_tsig);
+ REQUIRE(rdata->rdclass == dns_rdataclass_any);
+ 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 = ((uint64_t)sr.base[0] << 40) |
+ ((uint64_t)sr.base[1] << 32) |
+ ((uint64_t)sr.base[2] << 24) |
+ ((uint64_t)sr.base[3] << 16) |
+ ((uint64_t)sr.base[4] << 8) |
+ (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.rdtype == dns_rdatatype_tsig);
+ REQUIRE(tsig->common.rdclass == dns_rdataclass_any);
+
+ 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 == dns_rdatatype_tsig);
+ REQUIRE(rdata->rdclass == dns_rdataclass_any);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_any_tsig(ARGS_DIGEST) {
+
+ REQUIRE(rdata->type == dns_rdatatype_tsig);
+ REQUIRE(rdata->rdclass == dns_rdataclass_any);
+
+ UNUSED(rdata);
+ UNUSED(digest);
+ UNUSED(arg);
+
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static inline bool
+checkowner_any_tsig(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_tsig);
+ REQUIRE(rdclass == dns_rdataclass_any);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_any_tsig(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_tsig);
+ REQUIRE(rdata->rdclass == dns_rdataclass_any);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_any_tsig(ARGS_COMPARE) {
+ return (compare_any_tsig(rdata1, rdata2));
+}
+
+#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..6ace1d2
--- /dev/null
+++ b/lib/dns/rdata/any_255/tsig_250.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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;
+ uint64_t timesigned;
+ uint16_t fudge;
+ uint16_t siglen;
+ unsigned char * signature;
+ uint16_t originalid;
+ uint16_t error;
+ 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..94cae92
--- /dev/null
+++ b/lib/dns/rdata/ch_3/a_1.c
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/* 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 == dns_rdatatype_a);
+ REQUIRE(rdclass == dns_rdataclass_ch); /* 3 */
+
+ UNUSED(type);
+ UNUSED(callbacks);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ false));
+
+ /* get domain name */
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ if (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) {
+ bool ok;
+ ok = dns_name_ishostname(&name, 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, 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;
+ bool sub;
+ char buf[sizeof("0177777")];
+ uint16_t addr;
+
+ REQUIRE(rdata->type == dns_rdatatype_a);
+ 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));
+
+ snprintf(buf, sizeof(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 == dns_rdatatype_a);
+ 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);
+
+ memmove(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 == dns_rdatatype_a);
+ 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);
+
+ memmove(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 == dns_rdatatype_a);
+ 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(region1.base, region2.base, 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 == dns_rdatatype_a);
+ 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 == dns_rdatatype_a);
+ 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 == dns_rdatatype_a);
+
+ 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 == dns_rdatatype_a);
+ 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 == dns_rdatatype_a);
+ 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 bool
+checkowner_ch_a(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_a);
+ REQUIRE(rdclass == dns_rdataclass_ch);
+
+ UNUSED(type);
+
+ return (dns_name_ishostname(name, wildcard));
+}
+
+static inline bool
+checknames_ch_a(ARGS_CHECKNAMES) {
+ isc_region_t region;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == dns_rdatatype_a);
+ 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, false)) {
+ if (bad != NULL)
+ dns_name_clone(&name, bad);
+ return (false);
+ }
+
+ return (true);
+}
+
+static inline int
+casecompare_ch_a(ARGS_COMPARE) {
+ return (compare_ch_a(rdata1, rdata2));
+}
+#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..a969534
--- /dev/null
+++ b/lib/dns/rdata/ch_3/a_1.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/* 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 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..812dfa6
--- /dev/null
+++ b/lib/dns/rdata/generic/afsdb_18.c
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* 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;
+ bool ok;
+
+ REQUIRE(type == dns_rdatatype_afsdb);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ /*
+ * Subtype.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ 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,
+ false));
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ if (origin == NULL)
+ origin = dns_rootname;
+ RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target));
+ ok = true;
+ if ((options & DNS_RDATA_CHECKNAMES) != 0)
+ ok = dns_name_ishostname(&name, 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 ")];
+ bool sub;
+ unsigned int num;
+
+ REQUIRE(rdata->type == dns_rdatatype_afsdb);
+ 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);
+ snprintf(buf, sizeof(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 == dns_rdatatype_afsdb);
+
+ 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);
+ memmove(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 == dns_rdatatype_afsdb);
+ 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);
+ memmove(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 == dns_rdatatype_afsdb);
+ 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 == dns_rdatatype_afsdb);
+ 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 == dns_rdatatype_afsdb);
+ 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 == dns_rdatatype_afsdb);
+
+ 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 == dns_rdatatype_afsdb);
+
+ 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 == dns_rdatatype_afsdb);
+
+ 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 bool
+checkowner_afsdb(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_afsdb);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_afsdb(ARGS_CHECKNAMES) {
+ isc_region_t region;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == dns_rdatatype_afsdb);
+
+ 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, false)) {
+ if (bad != NULL)
+ dns_name_clone(&name, bad);
+ return (false);
+ }
+ return (true);
+}
+
+static inline int
+casecompare_afsdb(ARGS_COMPARE) {
+ return (compare_afsdb(rdata1, rdata2));
+}
+#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..24da1aa
--- /dev/null
+++ b/lib/dns/rdata/generic/afsdb_18.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef GENERIC_AFSDB_18_H
+#define GENERIC_AFSDB_18_H 1
+
+
+/*!
+ * \brief Per RFC1183 */
+
+typedef struct dns_rdata_afsdb {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ uint16_t subtype;
+ dns_name_t server;
+} dns_rdata_afsdb_t;
+
+#endif /* GENERIC_AFSDB_18_H */
+
diff --git a/lib/dns/rdata/generic/avc_258.c b/lib/dns/rdata/generic/avc_258.c
new file mode 100644
index 0000000..31bbaff
--- /dev/null
+++ b/lib/dns/rdata/generic/avc_258.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef RDATA_GENERIC_AVC_258_C
+#define RDATA_GENERIC_AVC_258_C
+
+#define RRTYPE_AVC_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_avc(ARGS_FROMTEXT) {
+
+ REQUIRE(type == dns_rdatatype_avc);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(callbacks);
+
+ return (generic_fromtext_txt(rdclass, type, lexer, origin, options,
+ target, callbacks));
+}
+
+static inline isc_result_t
+totext_avc(ARGS_TOTEXT) {
+
+ UNUSED(tctx);
+
+ REQUIRE(rdata->type == dns_rdatatype_avc);
+
+ return (generic_totext_txt(rdata, tctx, target));
+}
+
+static inline isc_result_t
+fromwire_avc(ARGS_FROMWIRE) {
+
+ REQUIRE(type == dns_rdatatype_avc);
+
+ UNUSED(type);
+ UNUSED(dctx);
+ UNUSED(rdclass);
+ UNUSED(options);
+
+ return (generic_fromwire_txt(rdclass, type, source, dctx, options,
+ target));
+}
+
+static inline isc_result_t
+towire_avc(ARGS_TOWIRE) {
+
+ REQUIRE(rdata->type == dns_rdatatype_avc);
+
+ UNUSED(cctx);
+
+ return (mem_tobuffer(target, rdata->data, rdata->length));
+}
+
+static inline int
+compare_avc(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == dns_rdatatype_avc);
+
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+ return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+fromstruct_avc(ARGS_FROMSTRUCT) {
+
+ REQUIRE(type == dns_rdatatype_avc);
+
+ return (generic_fromstruct_txt(rdclass, type, source, target));
+}
+
+static inline isc_result_t
+tostruct_avc(ARGS_TOSTRUCT) {
+ dns_rdata_avc_t *avc = target;
+
+ REQUIRE(rdata->type == dns_rdatatype_avc);
+ REQUIRE(target != NULL);
+
+ avc->common.rdclass = rdata->rdclass;
+ avc->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&avc->common, link);
+
+ return (generic_tostruct_txt(rdata, target, mctx));
+}
+
+static inline void
+freestruct_avc(ARGS_FREESTRUCT) {
+ dns_rdata_avc_t *txt = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(txt->common.rdtype == dns_rdatatype_avc);
+
+ generic_freestruct_txt(source);
+}
+
+static inline isc_result_t
+additionaldata_avc(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == dns_rdatatype_avc);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_avc(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == dns_rdatatype_avc);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_avc(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_avc);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_avc(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_avc);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_avc(ARGS_COMPARE) {
+ return (compare_avc(rdata1, rdata2));
+}
+#endif /* RDATA_GENERIC_AVC_258_C */
diff --git a/lib/dns/rdata/generic/avc_258.h b/lib/dns/rdata/generic/avc_258.h
new file mode 100644
index 0000000..88abadd
--- /dev/null
+++ b/lib/dns/rdata/generic/avc_258.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef GENERIC_AVC_258_H
+#define GENERIC_AVC_258_H 1
+
+typedef dns_rdata_txt_string_t dns_rdata_avc_string_t;
+
+typedef struct dns_rdata_avc {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ unsigned char *data;
+ uint16_t length;
+ /* private */
+ uint16_t offset;
+} dns_rdata_avc_t;
+
+/*
+ * ISC_LANG_BEGINDECLS and ISC_LANG_ENDDECLS are already done
+ * via rdatastructpre.h and rdatastructsuf.h.
+ */
+#endif /* GENERIC_AVC_258_H */
diff --git a/lib/dns/rdata/generic/caa_257.c b/lib/dns/rdata/generic/caa_257.c
new file mode 100644
index 0000000..4dc3639
--- /dev/null
+++ b/lib/dns/rdata/generic/caa_257.c
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef GENERIC_CAA_257_C
+#define GENERIC_CAA_257_C 1
+
+#define RRTYPE_CAA_ATTRIBUTES (0)
+
+static unsigned char const alphanumeric[256] = {
+ /* 0x00-0x0f */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0x10-0x1f */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0x20-0x2f */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0x30-0x3f */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
+ /* 0x40-0x4f */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 0x50-0x5f */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
+ /* 0x60-0x6f */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 0x70-0x7f */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
+ /* 0x80-0x8f */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0x90-0x9f */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0xa0-0xaf */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0xb0-0xbf */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0xc0-0xcf */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0xd0-0xdf */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0xe0-0xef */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0xf0-0xff */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+static inline isc_result_t
+fromtext_caa(ARGS_FROMTEXT) {
+ isc_token_t token;
+ isc_textregion_t tr;
+ uint8_t flags;
+ unsigned int i;
+
+ REQUIRE(type == dns_rdatatype_caa);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(callbacks);
+
+ /* Flags. */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ false));
+ if (token.value.as_ulong > 255U)
+ RETTOK(ISC_R_RANGE);
+ flags = (uint8_t)(token.value.as_ulong & 255U);
+ RETERR(uint8_tobuffer(flags, target));
+
+ /*
+ * Tag
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ false));
+ tr = token.value.as_textregion;
+ for (i = 0; i < tr.length; i++)
+ if (!alphanumeric[(unsigned char) tr.base[i]])
+ RETTOK(DNS_R_SYNTAX);
+ RETERR(uint8_tobuffer(tr.length, target));
+ RETERR(mem_tobuffer(target, tr.base, tr.length));
+
+ /*
+ * Value
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token,
+ isc_tokentype_qstring, false));
+ if (token.type != isc_tokentype_qstring &&
+ token.type != isc_tokentype_string)
+ RETERR(DNS_R_SYNTAX);
+ RETERR(multitxt_fromtext(&token.value.as_textregion, target));
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_caa(ARGS_TOTEXT) {
+ isc_region_t region;
+ uint8_t flags;
+ char buf[256];
+
+ UNUSED(tctx);
+
+ REQUIRE(rdata->type == dns_rdatatype_caa);
+ REQUIRE(rdata->length >= 3U);
+ REQUIRE(rdata->data != NULL);
+
+ dns_rdata_toregion(rdata, &region);
+
+ /*
+ * Flags
+ */
+ flags = uint8_consume_fromregion(&region);
+ snprintf(buf, sizeof(buf), "%u ", flags);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * Tag
+ */
+ RETERR(txt_totext(&region, false, target));
+ RETERR(str_totext(" ", target));
+
+ /*
+ * Value
+ */
+ RETERR(multitxt_totext(&region, target));
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+fromwire_caa(ARGS_FROMWIRE) {
+ isc_region_t sr;
+ unsigned int len, i;
+
+ REQUIRE(type == dns_rdatatype_caa);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(dctx);
+ UNUSED(options);
+
+ /*
+ * Flags
+ */
+ isc_buffer_activeregion(source, &sr);
+ if (sr.length < 2)
+ return (ISC_R_UNEXPECTEDEND);
+
+ /*
+ * Flags, tag length
+ */
+ RETERR(mem_tobuffer(target, sr.base, 2));
+ len = sr.base[1];
+ isc_region_consume(&sr, 2);
+ isc_buffer_forward(source, 2);
+
+ /*
+ * Zero length tag fields are illegal.
+ */
+ if (sr.length < len || len == 0)
+ RETERR(DNS_R_FORMERR);
+
+ /* Check the Tag's value */
+ for (i = 0; i < len; i++)
+ if (!alphanumeric[sr.base[i]])
+ RETERR(DNS_R_FORMERR);
+ /*
+ * Tag + Value
+ */
+ isc_buffer_forward(source, sr.length);
+ return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline isc_result_t
+towire_caa(ARGS_TOWIRE) {
+ isc_region_t region;
+
+ REQUIRE(rdata->type == dns_rdatatype_caa);
+ REQUIRE(rdata->length >= 3U);
+ REQUIRE(rdata->data != NULL);
+
+ UNUSED(cctx);
+
+ dns_rdata_toregion(rdata, &region);
+ return (mem_tobuffer(target, region.base, region.length));
+}
+
+static inline int
+compare_caa(ARGS_COMPARE) {
+ isc_region_t r1, r2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == dns_rdatatype_caa);
+ REQUIRE(rdata1->length >= 3U);
+ REQUIRE(rdata2->length >= 3U);
+ REQUIRE(rdata1->data != NULL);
+ REQUIRE(rdata2->data != NULL);
+
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+ return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+fromstruct_caa(ARGS_FROMSTRUCT) {
+ dns_rdata_caa_t *caa = source;
+ isc_region_t region;
+ unsigned int i;
+
+ REQUIRE(type == dns_rdatatype_caa);
+ REQUIRE(source != NULL);
+ REQUIRE(caa->common.rdtype == type);
+ REQUIRE(caa->common.rdclass == rdclass);
+ REQUIRE(caa->tag != NULL && caa->tag_len != 0);
+ REQUIRE(caa->value != NULL);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ /*
+ * Flags
+ */
+ RETERR(uint8_tobuffer(caa->flags, target));
+
+ /*
+ * Tag length
+ */
+ RETERR(uint8_tobuffer(caa->tag_len, target));
+
+ /*
+ * Tag
+ */
+ region.base = caa->tag;
+ region.length = caa->tag_len;
+ for (i = 0; i < region.length; i++)
+ if (!alphanumeric[region.base[i]])
+ RETERR(DNS_R_SYNTAX);
+ RETERR(isc_buffer_copyregion(target, &region));
+
+ /*
+ * Value
+ */
+ region.base = caa->value;
+ region.length = caa->value_len;
+ return (isc_buffer_copyregion(target, &region));
+}
+
+static inline isc_result_t
+tostruct_caa(ARGS_TOSTRUCT) {
+ dns_rdata_caa_t *caa = target;
+ isc_region_t sr;
+
+ REQUIRE(rdata->type == dns_rdatatype_caa);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length >= 3U);
+ REQUIRE(rdata->data != NULL);
+
+ caa->common.rdclass = rdata->rdclass;
+ caa->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&caa->common, link);
+
+ dns_rdata_toregion(rdata, &sr);
+
+ /*
+ * Flags
+ */
+ if (sr.length < 1)
+ return (ISC_R_UNEXPECTEDEND);
+ caa->flags = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+
+ /*
+ * Tag length
+ */
+ if (sr.length < 1)
+ return (ISC_R_UNEXPECTEDEND);
+ caa->tag_len = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+
+ /*
+ * Tag
+ */
+ if (sr.length < caa->tag_len)
+ return (ISC_R_UNEXPECTEDEND);
+ caa->tag = mem_maybedup(mctx, sr.base, caa->tag_len);
+ if (caa->tag == NULL)
+ return (ISC_R_NOMEMORY);
+ isc_region_consume(&sr, caa->tag_len);
+
+ /*
+ * Value
+ */
+ caa->value_len = sr.length;
+ caa->value = mem_maybedup(mctx, sr.base, sr.length);
+ if (caa->value == NULL)
+ return (ISC_R_NOMEMORY);
+
+ caa->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_caa(ARGS_FREESTRUCT) {
+ dns_rdata_caa_t *caa = (dns_rdata_caa_t *) source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(caa->common.rdtype == dns_rdatatype_caa);
+
+ if (caa->mctx == NULL)
+ return;
+
+ if (caa->tag != NULL)
+ isc_mem_free(caa->mctx, caa->tag);
+ if (caa->value != NULL)
+ isc_mem_free(caa->mctx, caa->value);
+ caa->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_caa(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == dns_rdatatype_caa);
+ REQUIRE(rdata->data != NULL);
+ REQUIRE(rdata->length >= 3U);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_caa(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == dns_rdatatype_caa);
+ REQUIRE(rdata->data != NULL);
+ REQUIRE(rdata->length >= 3U);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_caa(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_caa);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_caa(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_caa);
+ REQUIRE(rdata->data != NULL);
+ REQUIRE(rdata->length >= 3U);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_caa(ARGS_COMPARE) {
+ return (compare_caa(rdata1, rdata2));
+}
+
+#endif /* GENERIC_CAA_257_C */
diff --git a/lib/dns/rdata/generic/caa_257.h b/lib/dns/rdata/generic/caa_257.h
new file mode 100644
index 0000000..c16e427
--- /dev/null
+++ b/lib/dns/rdata/generic/caa_257.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef GENERIC_CAA_257_H
+#define GENERIC_CAA_257_H 1
+
+
+typedef struct dns_rdata_caa {
+ dns_rdatacommon_t common;
+ isc_mem_t * mctx;
+ uint8_t flags;
+ unsigned char * tag;
+ uint8_t tag_len;
+ unsigned char *value;
+ uint16_t value_len;
+} dns_rdata_caa_t;
+
+#endif /* GENERIC_CAA_257_H */
diff --git a/lib/dns/rdata/generic/cdnskey_60.c b/lib/dns/rdata/generic/cdnskey_60.c
new file mode 100644
index 0000000..afcdbc2
--- /dev/null
+++ b/lib/dns/rdata/generic/cdnskey_60.c
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* draft-ietf-dnsop-delegation-trust-maintainance-14 */
+
+#ifndef RDATA_GENERIC_CDNSKEY_60_C
+#define RDATA_GENERIC_CDNSKEY_60_C
+
+#include <dst/dst.h>
+
+#define RRTYPE_CDNSKEY_ATTRIBUTES 0
+
+static inline isc_result_t
+fromtext_cdnskey(ARGS_FROMTEXT) {
+
+ REQUIRE(type == dns_rdatatype_cdnskey);
+
+ return (generic_fromtext_key(rdclass, type, lexer, origin,
+ options, target, callbacks));
+}
+
+static inline isc_result_t
+totext_cdnskey(ARGS_TOTEXT) {
+
+ REQUIRE(rdata != NULL);
+ REQUIRE(rdata->type == dns_rdatatype_cdnskey);
+
+ return (generic_totext_key(rdata, tctx, target));
+}
+
+static inline isc_result_t
+fromwire_cdnskey(ARGS_FROMWIRE) {
+
+ REQUIRE(type == dns_rdatatype_cdnskey);
+
+ return (generic_fromwire_key(rdclass, type, source, dctx,
+ options, target));
+}
+
+static inline isc_result_t
+towire_cdnskey(ARGS_TOWIRE) {
+ isc_region_t sr;
+
+ REQUIRE(rdata->type == dns_rdatatype_cdnskey);
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(cctx);
+
+ dns_rdata_toregion(rdata, &sr);
+ return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline int
+compare_cdnskey(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1 != NULL);
+ REQUIRE(rdata2 != NULL);
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == dns_rdatatype_cdnskey);
+ 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_cdnskey(ARGS_FROMSTRUCT) {
+
+ REQUIRE(type == dns_rdatatype_cdnskey);
+
+ return (generic_fromstruct_key(rdclass, type, source, target));
+}
+
+static inline isc_result_t
+tostruct_cdnskey(ARGS_TOSTRUCT) {
+ dns_rdata_cdnskey_t *dnskey = target;
+
+ REQUIRE(dnskey != NULL);
+ REQUIRE(rdata != NULL);
+ REQUIRE(rdata->type == dns_rdatatype_cdnskey);
+
+ dnskey->common.rdclass = rdata->rdclass;
+ dnskey->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&dnskey->common, link);
+
+ return (generic_tostruct_key(rdata, target, mctx));
+}
+
+static inline void
+freestruct_cdnskey(ARGS_FREESTRUCT) {
+ dns_rdata_cdnskey_t *dnskey = (dns_rdata_cdnskey_t *) source;
+
+ REQUIRE(dnskey != NULL);
+ REQUIRE(dnskey->common.rdtype == dns_rdatatype_cdnskey);
+
+ generic_freestruct_key(source);
+}
+
+static inline isc_result_t
+additionaldata_cdnskey(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == dns_rdatatype_cdnskey);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_cdnskey(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata != NULL);
+ REQUIRE(rdata->type == dns_rdatatype_cdnskey);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_cdnskey(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_cdnskey);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_cdnskey(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata != NULL);
+ REQUIRE(rdata->type == dns_rdatatype_cdnskey);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_cdnskey(ARGS_COMPARE) {
+
+ /*
+ * Treat ALG 253 (private DNS) subtype name case sensistively.
+ */
+ return (compare_cdnskey(rdata1, rdata2));
+}
+
+#endif /* RDATA_GENERIC_CDNSKEY_60_C */
diff --git a/lib/dns/rdata/generic/cdnskey_60.h b/lib/dns/rdata/generic/cdnskey_60.h
new file mode 100644
index 0000000..541a4c2
--- /dev/null
+++ b/lib/dns/rdata/generic/cdnskey_60.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef GENERIC_CDNSKEY_60_H
+#define GENERIC_CDNSKEY_60_H 1
+
+/* CDNSKEY records have the same RDATA fields as DNSKEY records. */
+typedef struct dns_rdata_key dns_rdata_cdnskey_t;
+
+#endif /* GENERIC_CDNSKEY_60_H */
diff --git a/lib/dns/rdata/generic/cds_59.c b/lib/dns/rdata/generic/cds_59.c
new file mode 100644
index 0000000..e1ea7ce
--- /dev/null
+++ b/lib/dns/rdata/generic/cds_59.c
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* draft-ietf-dnsop-delegation-trust-maintainance-14 */
+
+#ifndef RDATA_GENERIC_CDS_59_C
+#define RDATA_GENERIC_CDS_59_C
+
+#define RRTYPE_CDS_ATTRIBUTES 0
+
+#include <isc/sha1.h>
+#include <isc/sha2.h>
+
+#include <dns/ds.h>
+
+#include "dst_gost.h"
+
+static inline isc_result_t
+fromtext_cds(ARGS_FROMTEXT) {
+
+ REQUIRE(type == dns_rdatatype_cds);
+
+ return (generic_fromtext_ds(rdclass, type, lexer, origin, options,
+ target, callbacks));
+}
+
+static inline isc_result_t
+totext_cds(ARGS_TOTEXT) {
+
+ REQUIRE(rdata->type == dns_rdatatype_cds);
+
+ return (generic_totext_ds(rdata, tctx, target));
+}
+
+static inline isc_result_t
+fromwire_cds(ARGS_FROMWIRE) {
+
+ REQUIRE(type == dns_rdatatype_cds);
+
+ return (generic_fromwire_ds(rdclass, type, source, dctx, options,
+ target));
+}
+
+static inline isc_result_t
+towire_cds(ARGS_TOWIRE) {
+ isc_region_t sr;
+
+ REQUIRE(rdata->type == dns_rdatatype_cds);
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(cctx);
+
+ dns_rdata_toregion(rdata, &sr);
+ return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline int
+compare_cds(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == dns_rdatatype_cds);
+ 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_cds(ARGS_FROMSTRUCT) {
+
+ REQUIRE(type == dns_rdatatype_cds);
+
+ return (generic_fromstruct_ds(rdclass, type, source, target));
+}
+
+static inline isc_result_t
+tostruct_cds(ARGS_TOSTRUCT) {
+ dns_rdata_cds_t *cds = target;
+
+ REQUIRE(rdata->type == dns_rdatatype_cds);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ /*
+ * Checked by generic_tostruct_ds().
+ */
+ cds->common.rdclass = rdata->rdclass;
+ cds->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&cds->common, link);
+
+ return (generic_tostruct_ds(rdata, target, mctx));
+}
+
+static inline void
+freestruct_cds(ARGS_FREESTRUCT) {
+ dns_rdata_cds_t *ds = source;
+
+ REQUIRE(ds != NULL);
+ REQUIRE(ds->common.rdtype == dns_rdatatype_cds);
+
+ 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_cds(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == dns_rdatatype_cds);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_cds(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == dns_rdatatype_cds);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_cds(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_cds);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_cds(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_cds);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_cds(ARGS_COMPARE) {
+ return (compare_cds(rdata1, rdata2));
+}
+
+#endif /* RDATA_GENERIC_CDS_59_C */
diff --git a/lib/dns/rdata/generic/cds_59.h b/lib/dns/rdata/generic/cds_59.h
new file mode 100644
index 0000000..0797bca
--- /dev/null
+++ b/lib/dns/rdata/generic/cds_59.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef GENERIC_CDS_59_H
+#define GENERIC_CDS_59_H 1
+
+/* CDS records have the same RDATA fields as DS records. */
+typedef struct dns_rdata_ds dns_rdata_cds_t;
+
+#endif /* GENERIC_CDS_59_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..237a806
--- /dev/null
+++ b/lib/dns/rdata/generic/cert_37.c
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* 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 == dns_rdatatype_cert);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(callbacks);
+
+ /*
+ * Cert type.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ 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,
+ 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,
+ 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 == dns_rdatatype_cert);
+ 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);
+ snprintf(buf, sizeof(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));
+ if (tctx->width == 0) /* No splitting */
+ RETERR(isc_base64_totext(&sr, 60, "", target));
+ else
+ 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 == dns_rdatatype_cert);
+
+ 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 == dns_rdatatype_cert);
+ 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 == dns_rdatatype_cert);
+ 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 == dns_rdatatype_cert);
+ 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 == dns_rdatatype_cert);
+ 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 == dns_rdatatype_cert);
+
+ 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 == dns_rdatatype_cert);
+
+ 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 == dns_rdatatype_cert);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_cert(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_cert);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_cert(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_cert);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+
+static inline int
+casecompare_cert(ARGS_COMPARE) {
+ return (compare_cert(rdata1, rdata2));
+}
+#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..db760e1
--- /dev/null
+++ b/lib/dns/rdata/generic/cert_37.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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;
+ uint16_t type;
+ uint16_t key_tag;
+ uint8_t algorithm;
+ 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..3da82eb
--- /dev/null
+++ b/lib/dns/rdata/generic/cname_5.c
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#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 == dns_rdatatype_cname);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ false));
+
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ if (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;
+ bool sub;
+
+ REQUIRE(rdata->type == dns_rdatatype_cname);
+ 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 == dns_rdatatype_cname);
+
+ 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 == dns_rdatatype_cname);
+ 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 == dns_rdatatype_cname);
+ 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 == dns_rdatatype_cname);
+ 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 == dns_rdatatype_cname);
+ 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 == dns_rdatatype_cname);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_cname(ARGS_DIGEST) {
+ isc_region_t r;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == dns_rdatatype_cname);
+
+ dns_rdata_toregion(rdata, &r);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &r);
+
+ return (dns_name_digest(&name, digest, arg));
+}
+
+static inline bool
+checkowner_cname(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_cname);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_cname(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_cname);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_cname(ARGS_COMPARE) {
+ return (compare_cname(rdata1, rdata2));
+}
+
+#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..6a5b645
--- /dev/null
+++ b/lib/dns/rdata/generic/cname_5.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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/csync_62.c b/lib/dns/rdata/generic/csync_62.c
new file mode 100644
index 0000000..c639128
--- /dev/null
+++ b/lib/dns/rdata/generic/csync_62.c
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* RFC 7477 */
+
+#ifndef RDATA_GENERIC_CSYNC_62_C
+#define RDATA_GENERIC_CSYNC_62_C
+
+#define RRTYPE_CSYNC_ATTRIBUTES 0
+
+static inline isc_result_t
+fromtext_csync(ARGS_FROMTEXT) {
+ isc_token_t token;
+
+ REQUIRE(type == dns_rdatatype_csync);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(callbacks);
+
+ /* Serial. */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ false));
+ RETERR(uint32_tobuffer(token.value.as_ulong, target));
+
+ /* Flags. */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ false));
+ if (token.value.as_ulong > 0xffffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint16_tobuffer(token.value.as_ulong, target));
+
+ /* Type Map */
+ return (typemap_fromtext(lexer, target, true));
+}
+
+static inline isc_result_t
+totext_csync(ARGS_TOTEXT) {
+ unsigned long num;
+ char buf[sizeof("0123456789")]; /* Also TYPE65535 */
+ isc_region_t sr;
+
+ REQUIRE(rdata->type == dns_rdatatype_csync);
+ REQUIRE(rdata->length >= 6);
+
+ UNUSED(tctx);
+
+ dns_rdata_toregion(rdata, &sr);
+
+ num = uint32_fromregion(&sr);
+ isc_region_consume(&sr, 4);
+ snprintf(buf, sizeof(buf), "%lu", num);
+ RETERR(str_totext(buf, target));
+
+ RETERR(str_totext(" ", target));
+
+ num = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+ snprintf(buf, sizeof(buf), "%lu", num);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * Don't leave a trailing space when there's no typemap present.
+ */
+ if (sr.length > 0) {
+ RETERR(str_totext(" ", target));
+ }
+ return (typemap_totext(&sr, NULL, target));
+}
+
+static /* inline */ isc_result_t
+fromwire_csync(ARGS_FROMWIRE) {
+ isc_region_t sr;
+
+ REQUIRE(type == dns_rdatatype_csync);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(options);
+ UNUSED(dctx);
+
+ /*
+ * Serial + Flags
+ */
+ 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);
+ isc_region_consume(&sr, 6);
+
+ RETERR(typemap_test(&sr, true));
+
+ RETERR(mem_tobuffer(target, sr.base, sr.length));
+ isc_buffer_forward(source, sr.length);
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+towire_csync(ARGS_TOWIRE) {
+
+ REQUIRE(rdata->type == dns_rdatatype_csync);
+ REQUIRE(rdata->length >= 6);
+
+ UNUSED(cctx);
+
+ return (mem_tobuffer(target, rdata->data, rdata->length));
+}
+
+static inline int
+compare_csync(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == dns_rdatatype_csync);
+ REQUIRE(rdata1->length >= 6);
+ REQUIRE(rdata2->length >= 6);
+
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+ return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+fromstruct_csync(ARGS_FROMSTRUCT) {
+ dns_rdata_csync_t *csync = source;
+ isc_region_t region;
+
+ REQUIRE(type == dns_rdatatype_csync);
+ REQUIRE(source != NULL);
+ REQUIRE(csync->common.rdtype == type);
+ REQUIRE(csync->common.rdclass == rdclass);
+ REQUIRE(csync->typebits != NULL || csync->len == 0);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ RETERR(uint32_tobuffer(csync->serial, target));
+ RETERR(uint16_tobuffer(csync->flags, target));
+
+ region.base = csync->typebits;
+ region.length = csync->len;
+ RETERR(typemap_test(&region, true));
+ return (mem_tobuffer(target, csync->typebits, csync->len));
+}
+
+static inline isc_result_t
+tostruct_csync(ARGS_TOSTRUCT) {
+ isc_region_t region;
+ dns_rdata_csync_t *csync = target;
+
+ REQUIRE(rdata->type == dns_rdatatype_csync);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ csync->common.rdclass = rdata->rdclass;
+ csync->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&csync->common, link);
+
+ dns_rdata_toregion(rdata, &region);
+
+ csync->serial = uint32_fromregion(&region);
+ isc_region_consume(&region, 4);
+
+ csync->flags = uint16_fromregion(&region);
+ isc_region_consume(&region, 2);
+
+ csync->len = region.length;
+ csync->typebits = mem_maybedup(mctx, region.base, region.length);
+ if (csync->typebits == NULL)
+ goto cleanup;
+
+ csync->mctx = mctx;
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ return (ISC_R_NOMEMORY);
+}
+
+static inline void
+freestruct_csync(ARGS_FREESTRUCT) {
+ dns_rdata_csync_t *csync = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(csync->common.rdtype == dns_rdatatype_csync);
+
+ if (csync->mctx == NULL)
+ return;
+
+ if (csync->typebits != NULL)
+ isc_mem_free(csync->mctx, csync->typebits);
+ csync->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_csync(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == dns_rdatatype_csync);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_csync(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == dns_rdatatype_csync);
+
+ dns_rdata_toregion(rdata, &r);
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_csync(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_csync);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_csync(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_csync);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_csync(ARGS_COMPARE) {
+ isc_region_t region1;
+ isc_region_t region2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == dns_rdatatype_csync);
+ REQUIRE(rdata1->length >= 6);
+ REQUIRE(rdata2->length >= 6);
+
+ dns_rdata_toregion(rdata1, &region1);
+ dns_rdata_toregion(rdata2, &region2);
+ return (isc_region_compare(&region1, &region2));
+}
+#endif /* RDATA_GENERIC_CSYNC_62_C */
diff --git a/lib/dns/rdata/generic/csync_62.h b/lib/dns/rdata/generic/csync_62.h
new file mode 100644
index 0000000..a1f63d5
--- /dev/null
+++ b/lib/dns/rdata/generic/csync_62.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef GENERIC_CSYNC_62_H
+#define GENERIC_CSYNC_62_H 1
+
+/*!
+ * \brief Per RFC 7477
+ */
+
+typedef struct dns_rdata_csync {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ uint32_t serial;
+ uint16_t flags;
+ unsigned char *typebits;
+ uint16_t len;
+} dns_rdata_csync_t;
+
+#endif /* GENERIC_CSYNC_62_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..0f05ba8
--- /dev/null
+++ b/lib/dns/rdata/generic/dlv_32769.c
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/* RFC3658 */
+
+#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>
+
+#include "dst_gost.h"
+
+static inline isc_result_t
+fromtext_dlv(ARGS_FROMTEXT) {
+
+ REQUIRE(type == dns_rdatatype_dlv);
+
+ return (generic_fromtext_ds(rdclass, type, lexer, origin, options,
+ target, callbacks));
+}
+
+static inline isc_result_t
+totext_dlv(ARGS_TOTEXT) {
+
+ REQUIRE(rdata->type == dns_rdatatype_dlv);
+
+ return (generic_totext_ds(rdata, tctx, target));
+}
+
+static inline isc_result_t
+fromwire_dlv(ARGS_FROMWIRE) {
+
+ REQUIRE(type == dns_rdatatype_dlv);
+
+ return (generic_fromwire_ds(rdclass, type, source, dctx, options,
+ target));
+}
+
+static inline isc_result_t
+towire_dlv(ARGS_TOWIRE) {
+ isc_region_t sr;
+
+ REQUIRE(rdata->type == dns_rdatatype_dlv);
+ 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 == dns_rdatatype_dlv);
+ 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) {
+
+ REQUIRE(type == dns_rdatatype_dlv);
+
+ return (generic_fromstruct_ds(rdclass, type, source, target));
+}
+
+static inline isc_result_t
+tostruct_dlv(ARGS_TOSTRUCT) {
+ dns_rdata_dlv_t *dlv = target;
+
+ REQUIRE(rdata->type == dns_rdatatype_dlv);
+
+ dlv->common.rdclass = rdata->rdclass;
+ dlv->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&dlv->common, link);
+
+ return (generic_tostruct_ds(rdata, target, mctx));
+}
+
+static inline void
+freestruct_dlv(ARGS_FREESTRUCT) {
+ dns_rdata_dlv_t *dlv = source;
+
+ REQUIRE(dlv != NULL);
+ REQUIRE(dlv->common.rdtype == dns_rdatatype_dlv);
+
+ 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 == dns_rdatatype_dlv);
+
+ 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 == dns_rdatatype_dlv);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_dlv(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_dlv);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_dlv(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_dlv);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_dlv(ARGS_COMPARE) {
+ return (compare_dlv(rdata1, rdata2));
+}
+
+#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..b94df71
--- /dev/null
+++ b/lib/dns/rdata/generic/dlv_32769.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/* draft-ietf-dnsext-delegation-signer-05.txt */
+#ifndef GENERIC_DLV_32769_H
+#define GENERIC_DLV_32769_H 1
+
+typedef struct dns_rdata_ds 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..50c5838
--- /dev/null
+++ b/lib/dns/rdata/generic/dname_39.c
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* 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 == dns_rdatatype_dname);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ false));
+
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ if (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;
+ bool sub;
+
+ REQUIRE(rdata->type == dns_rdatatype_dname);
+ 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 == dns_rdatatype_dname);
+
+ 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 == dns_rdatatype_dname);
+ 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 == dns_rdatatype_dname);
+ 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 == dns_rdatatype_dname);
+ 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 == dns_rdatatype_dname);
+ 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 == dns_rdatatype_dname);
+
+ 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 == dns_rdatatype_dname);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_dname(ARGS_DIGEST) {
+ isc_region_t r;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == dns_rdatatype_dname);
+
+ dns_rdata_toregion(rdata, &r);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &r);
+
+ return (dns_name_digest(&name, digest, arg));
+}
+
+static inline bool
+checkowner_dname(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_dname);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_dname(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_dname);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_dname(ARGS_COMPARE) {
+ return (compare_dname(rdata1, rdata2));
+}
+#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..12b02e2
--- /dev/null
+++ b/lib/dns/rdata/generic/dname_39.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef GENERIC_DNAME_39_H
+#define GENERIC_DNAME_39_H 1
+
+
+/*!
+ * \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..f20a8f6
--- /dev/null
+++ b/lib/dns/rdata/generic/dnskey_48.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* 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) {
+
+ REQUIRE(type == dns_rdatatype_dnskey);
+
+ return (generic_fromtext_key(rdclass, type, lexer, origin,
+ options, target, callbacks));
+}
+
+static inline isc_result_t
+totext_dnskey(ARGS_TOTEXT) {
+
+ REQUIRE(rdata != NULL);
+ REQUIRE(rdata->type == dns_rdatatype_dnskey);
+
+ return (generic_totext_key(rdata, tctx, target));
+}
+
+static inline isc_result_t
+fromwire_dnskey(ARGS_FROMWIRE) {
+
+ REQUIRE(type == dns_rdatatype_dnskey);
+
+ return (generic_fromwire_key(rdclass, type, source, dctx,
+ options, target));
+}
+
+static inline isc_result_t
+towire_dnskey(ARGS_TOWIRE) {
+ isc_region_t sr;
+
+ REQUIRE(rdata != NULL);
+ REQUIRE(rdata->type == dns_rdatatype_dnskey);
+ 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 != NULL);
+ REQUIRE(rdata2 != NULL);
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == dns_rdatatype_dnskey);
+ 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) {
+
+ REQUIRE(type == dns_rdatatype_dnskey);
+
+ return (generic_fromstruct_key(rdclass, type, source, target));
+}
+
+static inline isc_result_t
+tostruct_dnskey(ARGS_TOSTRUCT) {
+ dns_rdata_dnskey_t *dnskey = target;
+
+ REQUIRE(dnskey != NULL);
+ REQUIRE(rdata != NULL);
+ REQUIRE(rdata->type == dns_rdatatype_dnskey);
+
+ dnskey->common.rdclass = rdata->rdclass;
+ dnskey->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&dnskey->common, link);
+
+ return (generic_tostruct_key(rdata, target, mctx));
+}
+
+static inline void
+freestruct_dnskey(ARGS_FREESTRUCT) {
+ dns_rdata_dnskey_t *dnskey = (dns_rdata_dnskey_t *) source;
+
+ REQUIRE(dnskey != NULL);
+ REQUIRE(dnskey->common.rdtype == dns_rdatatype_dnskey);
+
+ generic_freestruct_key(source);
+}
+
+static inline isc_result_t
+additionaldata_dnskey(ARGS_ADDLDATA) {
+
+ REQUIRE(rdata->type == dns_rdatatype_dnskey);
+
+ 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 != NULL);
+ REQUIRE(rdata->type == dns_rdatatype_dnskey);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_dnskey(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_dnskey);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_dnskey(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata != NULL);
+ REQUIRE(rdata->type == dns_rdatatype_dnskey);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_dnskey(ARGS_COMPARE) {
+
+ /*
+ * Treat ALG 253 (private DNS) subtype name case sensistively.
+ */
+ return (compare_dnskey(rdata1, rdata2));
+}
+
+#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..ac4a6de
--- /dev/null
+++ b/lib/dns/rdata/generic/dnskey_48.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef GENERIC_DNSKEY_48_H
+#define GENERIC_DNSKEY_48_H 1
+
+/*!
+ * \brief per RFC2535
+ */
+
+typedef struct dns_rdata_key dns_rdata_dnskey_t;
+
+#endif /* GENERIC_DNSKEY_48_H */
diff --git a/lib/dns/rdata/generic/doa_259.c b/lib/dns/rdata/generic/doa_259.c
new file mode 100644
index 0000000..16c0703
--- /dev/null
+++ b/lib/dns/rdata/generic/doa_259.c
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef RDATA_GENERIC_DOA_259_C
+#define RDATA_GENERIC_DOA_259_C
+
+#define RRTYPE_DOA_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_doa(ARGS_FROMTEXT) {
+ isc_token_t token;
+
+ REQUIRE(type == dns_rdatatype_doa);
+
+ UNUSED(rdclass);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(callbacks);
+
+ /*
+ * DOA-ENTERPRISE
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ false));
+ RETERR(uint32_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * DOA-TYPE
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ false));
+ RETERR(uint32_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * DOA-LOCATION
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ false));
+ if (token.value.as_ulong > 0xffU) {
+ RETTOK(ISC_R_RANGE);
+ }
+ RETERR(uint8_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * DOA-MEDIA-TYPE
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_qstring,
+ false));
+ RETTOK(txt_fromtext(&token.value.as_textregion, target));
+
+ /*
+ * DOA-DATA
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ false));
+ if (strcmp(DNS_AS_STR(token), "-") == 0) {
+ return (ISC_R_SUCCESS);
+ } else {
+ isc_lex_ungettoken(lexer, &token);
+ return (isc_base64_tobuffer(lexer, target, -1));
+ }
+}
+
+static inline isc_result_t
+totext_doa(ARGS_TOTEXT) {
+ char buf[sizeof("4294967295 ")];
+ isc_region_t region;
+ uint32_t n;
+
+ REQUIRE(rdata != NULL);
+ REQUIRE(rdata->type == dns_rdatatype_doa);
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(tctx);
+
+ dns_rdata_toregion(rdata, &region);
+
+ /*
+ * DOA-ENTERPRISE
+ */
+ n = uint32_fromregion(&region);
+ isc_region_consume(&region, 4);
+ snprintf(buf, sizeof(buf), "%u ", n);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * DOA-TYPE
+ */
+ n = uint32_fromregion(&region);
+ isc_region_consume(&region, 4);
+ snprintf(buf, sizeof(buf), "%u ", n);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * DOA-LOCATION
+ */
+ n = uint8_fromregion(&region);
+ isc_region_consume(&region, 1);
+ snprintf(buf, sizeof(buf), "%u ", n);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * DOA-MEDIA-TYPE
+ */
+ RETERR(txt_totext(&region, true, target));
+ RETERR(str_totext(" ", target));
+
+ /*
+ * DOA-DATA
+ */
+ if (region.length == 0) {
+ return (str_totext("-", target));
+ } else {
+ return (isc_base64_totext(&region, 60, "", target));
+ }
+}
+
+static inline isc_result_t
+fromwire_doa(ARGS_FROMWIRE) {
+ isc_region_t region;
+
+ UNUSED(rdclass);
+ UNUSED(dctx);
+ UNUSED(options);
+
+ REQUIRE(type == dns_rdatatype_doa);
+
+ isc_buffer_activeregion(source, &region);
+ /*
+ * DOA-MEDIA-TYPE may be an empty <character-string> (i.e.,
+ * comprising of just the length octet) and DOA-DATA can have
+ * zero length.
+ */
+ if (region.length < 4 + 4 + 1 + 1) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+
+ /*
+ * Check whether DOA-MEDIA-TYPE length is not malformed.
+ */
+ if (region.base[9] > region.length - 10) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+
+ isc_buffer_forward(source, region.length);
+ return (mem_tobuffer(target, region.base, region.length));
+}
+
+static inline isc_result_t
+towire_doa(ARGS_TOWIRE) {
+ isc_region_t region;
+
+ UNUSED(cctx);
+
+ REQUIRE(rdata != NULL);
+ REQUIRE(rdata->type == dns_rdatatype_doa);
+ REQUIRE(rdata->length != 0);
+
+ dns_rdata_toregion(rdata, &region);
+ return (mem_tobuffer(target, region.base, region.length));
+}
+
+static inline int
+compare_doa(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1 != NULL);
+ REQUIRE(rdata2 != NULL);
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->type == dns_rdatatype_doa);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ 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_doa(ARGS_FROMSTRUCT) {
+ dns_rdata_doa_t *doa = source;
+
+ REQUIRE(type == dns_rdatatype_doa);
+ REQUIRE(source != NULL);
+ REQUIRE(doa->common.rdtype == dns_rdatatype_doa);
+ REQUIRE(doa->common.rdclass == rdclass);
+
+ RETERR(uint32_tobuffer(doa->enterprise, target));
+ RETERR(uint32_tobuffer(doa->type, target));
+ RETERR(uint8_tobuffer(doa->location, target));
+ RETERR(uint8_tobuffer(doa->mediatype_len, target));
+ RETERR(mem_tobuffer(target, doa->mediatype, doa->mediatype_len));
+ return (mem_tobuffer(target, doa->data, doa->data_len));
+}
+
+static inline isc_result_t
+tostruct_doa(ARGS_TOSTRUCT) {
+ dns_rdata_doa_t *doa = target;
+ isc_region_t region;
+
+ REQUIRE(rdata != NULL);
+ REQUIRE(rdata->type == dns_rdatatype_doa);
+ REQUIRE(rdata->length != 0);
+
+ doa->common.rdclass = rdata->rdclass;
+ doa->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&doa->common, link);
+
+ dns_rdata_toregion(rdata, &region);
+
+ /*
+ * DOA-ENTERPRISE
+ */
+ if (region.length < 4) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+ doa->enterprise = uint32_fromregion(&region);
+ isc_region_consume(&region, 4);
+
+ /*
+ * DOA-TYPE
+ */
+ if (region.length < 4) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+ doa->type = uint32_fromregion(&region);
+ isc_region_consume(&region, 4);
+
+ /*
+ * DOA-LOCATION
+ */
+ if (region.length < 1) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+ doa->location = uint8_fromregion(&region);
+ isc_region_consume(&region, 1);
+
+ /*
+ * DOA-MEDIA-TYPE
+ */
+ if (region.length < 1) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+ doa->mediatype_len = uint8_fromregion(&region);
+ isc_region_consume(&region, 1);
+ INSIST(doa->mediatype_len <= region.length);
+ doa->mediatype = mem_maybedup(mctx, region.base, doa->mediatype_len);
+ if (doa->mediatype == NULL) {
+ goto cleanup;
+ }
+ isc_region_consume(&region, doa->mediatype_len);
+
+ /*
+ * DOA-DATA
+ */
+ doa->data_len = region.length;
+ doa->data = NULL;
+ if (doa->data_len > 0) {
+ doa->data = mem_maybedup(mctx, region.base, doa->data_len);
+ if (doa->data == NULL) {
+ goto cleanup;
+ }
+ isc_region_consume(&region, doa->data_len);
+ }
+
+ doa->mctx = mctx;
+
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ if (mctx != NULL && doa->mediatype != NULL) {
+ isc_mem_free(mctx, doa->mediatype);
+ }
+ return (ISC_R_NOMEMORY);
+}
+
+static inline void
+freestruct_doa(ARGS_FREESTRUCT) {
+ dns_rdata_doa_t *doa = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(doa->common.rdtype == dns_rdatatype_doa);
+
+ if (doa->mctx == NULL) {
+ return;
+ }
+
+ if (doa->mediatype != NULL) {
+ isc_mem_free(doa->mctx, doa->mediatype);
+ }
+ if (doa->data != NULL) {
+ isc_mem_free(doa->mctx, doa->data);
+ }
+
+ doa->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_doa(ARGS_ADDLDATA) {
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ REQUIRE(rdata->type == dns_rdatatype_doa);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_doa(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == dns_rdatatype_doa);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_doa(ARGS_CHECKOWNER) {
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ REQUIRE(type == dns_rdatatype_doa);
+
+ return (true);
+}
+
+static inline bool
+checknames_doa(ARGS_CHECKNAMES) {
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ REQUIRE(rdata->type == dns_rdatatype_doa);
+
+ return (true);
+}
+
+static inline int
+casecompare_doa(ARGS_COMPARE) {
+ return (compare_doa(rdata1, rdata2));
+}
+
+#endif /* RDATA_GENERIC_DOA_259_C */
diff --git a/lib/dns/rdata/generic/doa_259.h b/lib/dns/rdata/generic/doa_259.h
new file mode 100644
index 0000000..5ac5fbb
--- /dev/null
+++ b/lib/dns/rdata/generic/doa_259.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef GENERIC_DOA_259_H
+#define GENERIC_DOA_259_H 1
+
+typedef struct dns_rdata_doa {
+ dns_rdatacommon_t common;
+ isc_mem_t * mctx;
+ unsigned char * mediatype;
+ unsigned char * data;
+ uint32_t enterprise;
+ uint32_t type;
+ uint16_t data_len;
+ uint8_t location;
+ uint8_t mediatype_len;
+} dns_rdata_doa_t;
+
+#endif /* GENERIC_DOA_259_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..d25c1a6
--- /dev/null
+++ b/lib/dns/rdata/generic/ds_43.c
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/* RFC3658 */
+
+#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>
+
+#include "dst_gost.h"
+
+static inline isc_result_t
+generic_fromtext_ds(ARGS_FROMTEXT) {
+ isc_token_t token;
+ unsigned char c;
+ int length;
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(callbacks);
+
+ /*
+ * Key tag.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ 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,
+ 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_string,
+ false));
+ RETTOK(dns_dsdigest_fromtext(&c, &token.value.as_textregion));
+ RETERR(mem_tobuffer(target, &c, 1));
+
+ /*
+ * Digest.
+ */
+ switch (c) {
+ case DNS_DSDIGEST_SHA1:
+ length = ISC_SHA1_DIGESTLENGTH;
+ break;
+ case DNS_DSDIGEST_SHA256:
+ length = ISC_SHA256_DIGESTLENGTH;
+ break;
+#ifdef ISC_GOST_DIGESTLENGTH
+ case DNS_DSDIGEST_GOST:
+ length = ISC_GOST_DIGESTLENGTH;
+ break;
+#endif
+ case DNS_DSDIGEST_SHA384:
+ length = ISC_SHA384_DIGESTLENGTH;
+ break;
+ default:
+ length = -1;
+ break;
+ }
+ return (isc_hex_tobuffer(lexer, target, length));
+}
+
+static inline isc_result_t
+fromtext_ds(ARGS_FROMTEXT) {
+
+ REQUIRE(type == dns_rdatatype_ds);
+
+ return (generic_fromtext_ds(rdclass, type, lexer, origin, options,
+ target, callbacks));
+}
+
+static inline isc_result_t
+generic_totext_ds(ARGS_TOTEXT) {
+ isc_region_t sr;
+ char buf[sizeof("64000 ")];
+ unsigned int n;
+
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(tctx);
+
+ dns_rdata_toregion(rdata, &sr);
+
+ /*
+ * Key tag.
+ */
+ n = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+ snprintf(buf, sizeof(buf), "%u ", n);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * Algorithm.
+ */
+ n = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+ snprintf(buf, sizeof(buf), "%u ", n);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * Digest type.
+ */
+ n = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+ snprintf(buf, sizeof(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));
+ if ((tctx->flags & DNS_STYLEFLAG_NOCRYPTO) == 0) {
+ if (tctx->width == 0) /* No splitting */
+ RETERR(isc_hex_totext(&sr, 0, "", target));
+ else
+ RETERR(isc_hex_totext(&sr, tctx->width - 2,
+ tctx->linebreak, target));
+ } else
+ RETERR(str_totext("[omitted]", target));
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext(" )", target));
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_ds(ARGS_TOTEXT) {
+
+ REQUIRE(rdata->type == dns_rdatatype_ds);
+
+ return (generic_totext_ds(rdata, tctx, target));
+}
+
+static inline isc_result_t
+generic_fromwire_ds(ARGS_FROMWIRE) {
+ isc_region_t sr;
+
+ 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) ||
+#ifdef ISC_GOST_DIGESTLENGTH
+ (sr.base[3] == DNS_DSDIGEST_GOST &&
+ sr.length < 4 + ISC_GOST_DIGESTLENGTH) ||
+#endif
+ (sr.base[3] == DNS_DSDIGEST_SHA384 &&
+ sr.length < 4 + ISC_SHA384_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;
+#ifdef ISC_GOST_DIGESTLENGTH
+ else if (sr.base[3] == DNS_DSDIGEST_GOST)
+ sr.length = 4 + ISC_GOST_DIGESTLENGTH;
+#endif
+ else if (sr.base[3] == DNS_DSDIGEST_SHA384)
+ sr.length = 4 + ISC_SHA384_DIGESTLENGTH;
+
+ isc_buffer_forward(source, sr.length);
+ return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline isc_result_t
+fromwire_ds(ARGS_FROMWIRE) {
+
+ REQUIRE(type == dns_rdatatype_ds);
+
+ return (generic_fromwire_ds(rdclass, type, source, dctx, options,
+ target));
+}
+
+static inline isc_result_t
+towire_ds(ARGS_TOWIRE) {
+ isc_region_t sr;
+
+ REQUIRE(rdata->type == dns_rdatatype_ds);
+ 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 == dns_rdatatype_ds);
+ 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
+generic_fromstruct_ds(ARGS_FROMSTRUCT) {
+ dns_rdata_ds_t *ds = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(ds->common.rdtype == type);
+ REQUIRE(ds->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(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;
+#ifdef ISC_GOST_DIGESTLENGTH
+ case DNS_DSDIGEST_GOST:
+ REQUIRE(ds->length == ISC_GOST_DIGESTLENGTH);
+ break;
+#endif
+ case DNS_DSDIGEST_SHA384:
+ REQUIRE(ds->length == ISC_SHA384_DIGESTLENGTH);
+ break;
+ }
+
+ 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
+fromstruct_ds(ARGS_FROMSTRUCT) {
+
+ REQUIRE(type == dns_rdatatype_ds);
+
+ return (generic_fromstruct_ds(rdclass, type, source, target));
+}
+
+static inline isc_result_t
+generic_tostruct_ds(ARGS_TOSTRUCT) {
+ dns_rdata_ds_t *ds = target;
+ isc_region_t region;
+
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+ REQUIRE(ds->common.rdtype == rdata->type);
+ REQUIRE(ds->common.rdclass == rdata->rdclass);
+ REQUIRE(!ISC_LINK_LINKED(&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 isc_result_t
+tostruct_ds(ARGS_TOSTRUCT) {
+ dns_rdata_ds_t *ds = target;
+
+ REQUIRE(rdata->type == dns_rdatatype_ds);
+ REQUIRE(target != NULL);
+
+ ds->common.rdclass = rdata->rdclass;
+ ds->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&ds->common, link);
+
+ return (generic_tostruct_ds(rdata, target, mctx));
+}
+
+static inline void
+freestruct_ds(ARGS_FREESTRUCT) {
+ dns_rdata_ds_t *ds = source;
+
+ REQUIRE(ds != NULL);
+ REQUIRE(ds->common.rdtype == dns_rdatatype_ds);
+
+ 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 == dns_rdatatype_ds);
+
+ 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 == dns_rdatatype_ds);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_ds(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_ds);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_ds(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_ds);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_ds(ARGS_COMPARE) {
+ return (compare_ds(rdata1, rdata2));
+}
+
+#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..0d373ec
--- /dev/null
+++ b/lib/dns/rdata/generic/ds_43.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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;
+ uint16_t key_tag;
+ uint8_t algorithm;
+ uint8_t digest_type;
+ uint16_t length;
+ unsigned char *digest;
+} dns_rdata_ds_t;
+
+#endif /* GENERIC_DS_43_H */
diff --git a/lib/dns/rdata/generic/eui48_108.c b/lib/dns/rdata/generic/eui48_108.c
new file mode 100644
index 0000000..686f23f
--- /dev/null
+++ b/lib/dns/rdata/generic/eui48_108.c
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef RDATA_GENERIC_EUI48_108_C
+#define RDATA_GENERIC_EUI48_108_C
+
+#include <string.h>
+
+#define RRTYPE_EUI48_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_eui48(ARGS_FROMTEXT) {
+ isc_token_t token;
+ unsigned char eui48[6];
+ unsigned int l0, l1, l2, l3, l4, l5;
+ int n;
+
+ REQUIRE(type == dns_rdatatype_eui48);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(callbacks);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ false));
+ n = sscanf(DNS_AS_STR(token), "%2x-%2x-%2x-%2x-%2x-%2x",
+ &l0, &l1, &l2, &l3, &l4, &l5);
+ if (n != 6 || l0 > 255U || l1 > 255U || l2 > 255U || l3 > 255U ||
+ l4 > 255U || l5 > 255U)
+ return (DNS_R_BADEUI);
+
+ eui48[0] = l0;
+ eui48[1] = l1;
+ eui48[2] = l2;
+ eui48[3] = l3;
+ eui48[4] = l4;
+ eui48[5] = l5;
+ return (mem_tobuffer(target, eui48, sizeof(eui48)));
+}
+
+static inline isc_result_t
+totext_eui48(ARGS_TOTEXT) {
+ char buf[sizeof("xx-xx-xx-xx-xx-xx")];
+
+ REQUIRE(rdata->type == dns_rdatatype_eui48);
+ REQUIRE(rdata->length == 6);
+
+ UNUSED(tctx);
+
+ (void)snprintf(buf, sizeof(buf), "%02x-%02x-%02x-%02x-%02x-%02x",
+ rdata->data[0], rdata->data[1], rdata->data[2],
+ rdata->data[3], rdata->data[4], rdata->data[5]);
+ return (str_totext(buf, target));
+}
+
+static inline isc_result_t
+fromwire_eui48(ARGS_FROMWIRE) {
+ isc_region_t sregion;
+
+ REQUIRE(type == dns_rdatatype_eui48);
+
+ UNUSED(type);
+ UNUSED(options);
+ UNUSED(rdclass);
+ UNUSED(dctx);
+
+ isc_buffer_activeregion(source, &sregion);
+ if (sregion.length != 6)
+ return (DNS_R_FORMERR);
+ isc_buffer_forward(source, sregion.length);
+ return (mem_tobuffer(target, sregion.base, sregion.length));
+}
+
+static inline isc_result_t
+towire_eui48(ARGS_TOWIRE) {
+
+ REQUIRE(rdata->type == dns_rdatatype_eui48);
+ REQUIRE(rdata->length == 6);
+
+ UNUSED(cctx);
+
+ return (mem_tobuffer(target, rdata->data, rdata->length));
+}
+
+static inline int
+compare_eui48(ARGS_COMPARE) {
+ isc_region_t region1;
+ isc_region_t region2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == dns_rdatatype_eui48);
+ REQUIRE(rdata1->length == 6);
+ REQUIRE(rdata2->length == 6);
+
+ dns_rdata_toregion(rdata1, &region1);
+ dns_rdata_toregion(rdata2, &region2);
+ return (isc_region_compare(&region1, &region2));
+}
+
+static inline isc_result_t
+fromstruct_eui48(ARGS_FROMSTRUCT) {
+ dns_rdata_eui48_t *eui48 = source;
+
+ REQUIRE(type == dns_rdatatype_eui48);
+ REQUIRE(source != NULL);
+ REQUIRE(eui48->common.rdtype == type);
+ REQUIRE(eui48->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ return (mem_tobuffer(target, eui48->eui48, sizeof(eui48->eui48)));
+}
+
+static inline isc_result_t
+tostruct_eui48(ARGS_TOSTRUCT) {
+ dns_rdata_eui48_t *eui48 = target;
+
+ REQUIRE(rdata->type == dns_rdatatype_eui48);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length == 6);
+
+ UNUSED(mctx);
+
+ eui48->common.rdclass = rdata->rdclass;
+ eui48->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&eui48->common, link);
+
+ memmove(eui48->eui48, rdata->data, rdata->length);
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_eui48(ARGS_FREESTRUCT) {
+ dns_rdata_eui48_t *eui48 = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(eui48->common.rdtype == dns_rdatatype_eui48);
+
+ return;
+}
+
+static inline isc_result_t
+additionaldata_eui48(ARGS_ADDLDATA) {
+
+ REQUIRE(rdata->type == dns_rdatatype_eui48);
+ REQUIRE(rdata->length == 6);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_eui48(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == dns_rdatatype_eui48);
+ REQUIRE(rdata->length == 6);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_eui48(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_eui48);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_eui48(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_eui48);
+ REQUIRE(rdata->length == 6);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_eui48(ARGS_COMPARE) {
+ return (compare_eui48(rdata1, rdata2));
+}
+
+#endif /* RDATA_GENERIC_EUI48_108_C */
diff --git a/lib/dns/rdata/generic/eui48_108.h b/lib/dns/rdata/generic/eui48_108.h
new file mode 100644
index 0000000..1bb168a
--- /dev/null
+++ b/lib/dns/rdata/generic/eui48_108.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* */
+#ifndef GENERIC_EUI48_108_H
+#define GENERIC_EUI48_108_H 1
+
+typedef struct dns_rdata_eui48 {
+ dns_rdatacommon_t common;
+ unsigned char eui48[6];
+} dns_rdata_eui48_t;
+
+#endif /* GENERIC_EUI48_10k_H */
diff --git a/lib/dns/rdata/generic/eui64_109.c b/lib/dns/rdata/generic/eui64_109.c
new file mode 100644
index 0000000..0a37326
--- /dev/null
+++ b/lib/dns/rdata/generic/eui64_109.c
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef RDATA_GENERIC_EUI64_109_C
+#define RDATA_GENERIC_EUI64_109_C
+
+#include <string.h>
+
+#define RRTYPE_EUI64_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_eui64(ARGS_FROMTEXT) {
+ isc_token_t token;
+ unsigned char eui64[8];
+ unsigned int l0, l1, l2, l3, l4, l5, l6, l7;
+ int n;
+
+ REQUIRE(type == dns_rdatatype_eui64);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(callbacks);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ false));
+ n = sscanf(DNS_AS_STR(token), "%2x-%2x-%2x-%2x-%2x-%2x-%2x-%2x",
+ &l0, &l1, &l2, &l3, &l4, &l5, &l6, &l7);
+ if (n != 8 || l0 > 255U || l1 > 255U || l2 > 255U || l3 > 255U ||
+ l4 > 255U || l5 > 255U || l6 > 255U || l7 > 255U)
+ return (DNS_R_BADEUI);
+
+ eui64[0] = l0;
+ eui64[1] = l1;
+ eui64[2] = l2;
+ eui64[3] = l3;
+ eui64[4] = l4;
+ eui64[5] = l5;
+ eui64[6] = l6;
+ eui64[7] = l7;
+ return (mem_tobuffer(target, eui64, sizeof(eui64)));
+}
+
+static inline isc_result_t
+totext_eui64(ARGS_TOTEXT) {
+ char buf[sizeof("xx-xx-xx-xx-xx-xx-xx-xx")];
+
+ REQUIRE(rdata->type == dns_rdatatype_eui64);
+ REQUIRE(rdata->length == 8);
+
+ UNUSED(tctx);
+
+ (void)snprintf(buf, sizeof(buf),
+ "%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x",
+ rdata->data[0], rdata->data[1],
+ rdata->data[2], rdata->data[3],
+ rdata->data[4], rdata->data[5],
+ rdata->data[6], rdata->data[7]);
+ return (str_totext(buf, target));
+}
+
+static inline isc_result_t
+fromwire_eui64(ARGS_FROMWIRE) {
+ isc_region_t sregion;
+
+ REQUIRE(type == dns_rdatatype_eui64);
+
+ UNUSED(type);
+ UNUSED(options);
+ UNUSED(rdclass);
+ UNUSED(dctx);
+
+ isc_buffer_activeregion(source, &sregion);
+ if (sregion.length != 8)
+ return (DNS_R_FORMERR);
+ isc_buffer_forward(source, sregion.length);
+ return (mem_tobuffer(target, sregion.base, sregion.length));
+}
+
+static inline isc_result_t
+towire_eui64(ARGS_TOWIRE) {
+
+ REQUIRE(rdata->type == dns_rdatatype_eui64);
+ REQUIRE(rdata->length == 8);
+
+ UNUSED(cctx);
+
+ return (mem_tobuffer(target, rdata->data, rdata->length));
+}
+
+static inline int
+compare_eui64(ARGS_COMPARE) {
+ isc_region_t region1;
+ isc_region_t region2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == dns_rdatatype_eui64);
+ REQUIRE(rdata1->length == 8);
+ REQUIRE(rdata2->length == 8);
+
+ dns_rdata_toregion(rdata1, &region1);
+ dns_rdata_toregion(rdata2, &region2);
+ return (isc_region_compare(&region1, &region2));
+}
+
+static inline isc_result_t
+fromstruct_eui64(ARGS_FROMSTRUCT) {
+ dns_rdata_eui64_t *eui64 = source;
+
+ REQUIRE(type == dns_rdatatype_eui64);
+ REQUIRE(source != NULL);
+ REQUIRE(eui64->common.rdtype == type);
+ REQUIRE(eui64->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ return (mem_tobuffer(target, eui64->eui64, sizeof(eui64->eui64)));
+}
+
+static inline isc_result_t
+tostruct_eui64(ARGS_TOSTRUCT) {
+ dns_rdata_eui64_t *eui64 = target;
+
+ REQUIRE(rdata->type == dns_rdatatype_eui64);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length == 8);
+
+ UNUSED(mctx);
+
+ eui64->common.rdclass = rdata->rdclass;
+ eui64->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&eui64->common, link);
+
+ memmove(eui64->eui64, rdata->data, rdata->length);
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_eui64(ARGS_FREESTRUCT) {
+ dns_rdata_eui64_t *eui64 = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(eui64->common.rdtype == dns_rdatatype_eui64);
+
+ return;
+}
+
+static inline isc_result_t
+additionaldata_eui64(ARGS_ADDLDATA) {
+
+ REQUIRE(rdata->type == dns_rdatatype_eui64);
+ REQUIRE(rdata->length == 8);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_eui64(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == dns_rdatatype_eui64);
+ REQUIRE(rdata->length == 8);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_eui64(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_eui64);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_eui64(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_eui64);
+ REQUIRE(rdata->length == 8);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_eui64(ARGS_COMPARE) {
+ return (compare_eui64(rdata1, rdata2));
+}
+
+#endif /* RDATA_GENERIC_EUI64_109_C */
diff --git a/lib/dns/rdata/generic/eui64_109.h b/lib/dns/rdata/generic/eui64_109.h
new file mode 100644
index 0000000..638c72e
--- /dev/null
+++ b/lib/dns/rdata/generic/eui64_109.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* */
+#ifndef GENERIC_EUI64_109_H
+#define GENERIC_EUI64_109_H 1
+
+typedef struct dns_rdata_eui64 {
+ dns_rdatacommon_t common;
+ unsigned char eui64[8];
+} dns_rdata_eui64_t;
+
+#endif /* GENERIC_EUI64_10k_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..6242932
--- /dev/null
+++ b/lib/dns/rdata/generic/gpos_27.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* 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 == dns_rdatatype_gpos);
+
+ 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,
+ 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 == dns_rdatatype_gpos);
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(tctx);
+
+ dns_rdata_toregion(rdata, &region);
+
+ for (i = 0; i < 3; i++) {
+ RETERR(txt_totext(&region, true, 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 == dns_rdatatype_gpos);
+
+ 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 == dns_rdatatype_gpos);
+ 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 == dns_rdatatype_gpos);
+ 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 == dns_rdatatype_gpos);
+ 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 == dns_rdatatype_gpos);
+ 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 == dns_rdatatype_gpos);
+
+ 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 == dns_rdatatype_gpos);
+
+ 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 == dns_rdatatype_gpos);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_gpos(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_gpos);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_gpos(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_gpos);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_gpos(ARGS_COMPARE) {
+ return (compare_gpos(rdata1, rdata2));
+}
+
+#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..fac72cd
--- /dev/null
+++ b/lib/dns/rdata/generic/gpos_27.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef GENERIC_GPOS_27_H
+#define GENERIC_GPOS_27_H 1
+
+
+/*!
+ * \brief per RFC1712 */
+
+typedef struct dns_rdata_gpos {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ char *longitude;
+ char *latitude;
+ char *altitude;
+ uint8_t long_len;
+ uint8_t lat_len;
+ 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..528be5e
--- /dev/null
+++ b/lib/dns/rdata/generic/hinfo_13.c
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#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 == dns_rdatatype_hinfo);
+
+ for (i = 0; i < 2; i++) {
+ RETERR(isc_lex_getmastertoken(lexer, &token,
+ isc_tokentype_qstring,
+ 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 == dns_rdatatype_hinfo);
+ REQUIRE(rdata->length != 0);
+
+ dns_rdata_toregion(rdata, &region);
+ RETERR(txt_totext(&region, true, target));
+ RETERR(str_totext(" ", target));
+ return (txt_totext(&region, true, target));
+}
+
+static inline isc_result_t
+fromwire_hinfo(ARGS_FROMWIRE) {
+
+ REQUIRE(type == dns_rdatatype_hinfo);
+
+ 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 == dns_rdatatype_hinfo);
+ 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 == dns_rdatatype_hinfo);
+ 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 == dns_rdatatype_hinfo);
+ 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 == dns_rdatatype_hinfo);
+ 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 == dns_rdatatype_hinfo);
+
+ 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 == dns_rdatatype_hinfo);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_hinfo(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_hinfo);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_hinfo(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_hinfo);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_hinfo(ARGS_COMPARE) {
+ return (compare_hinfo(rdata1, rdata2));
+}
+#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..323be22
--- /dev/null
+++ b/lib/dns/rdata/generic/hinfo_13.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef GENERIC_HINFO_13_H
+#define GENERIC_HINFO_13_H 1
+
+
+typedef struct dns_rdata_hinfo {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ char *cpu;
+ char *os;
+ uint8_t cpu_len;
+ uint8_t os_len;
+} dns_rdata_hinfo_t;
+
+#endif /* GENERIC_HINFO_13_H */
diff --git a/lib/dns/rdata/generic/hip_55.c b/lib/dns/rdata/generic/hip_55.c
new file mode 100644
index 0000000..36c9273
--- /dev/null
+++ b/lib/dns/rdata/generic/hip_55.c
@@ -0,0 +1,497 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* RFC 5205 */
+
+#ifndef RDATA_GENERIC_HIP_5_C
+#define RDATA_GENERIC_HIP_5_C
+
+#define RRTYPE_HIP_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_hip(ARGS_FROMTEXT) {
+ isc_token_t token;
+ dns_name_t name;
+ isc_buffer_t buffer;
+ isc_buffer_t hit_len;
+ isc_buffer_t key_len;
+ unsigned char *start;
+ size_t len;
+
+ REQUIRE(type == dns_rdatatype_hip);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ /*
+ * Dummy HIT len.
+ */
+ hit_len = *target;
+ RETERR(uint8_tobuffer(0, target));
+
+ /*
+ * Algorithm.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ false));
+ if (token.value.as_ulong > 0xffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint8_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * Dummy KEY len.
+ */
+ key_len = *target;
+ RETERR(uint16_tobuffer(0, target));
+
+ /*
+ * HIT (base16).
+ */
+ start = isc_buffer_used(target);
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ false));
+ RETTOK(isc_hex_decodestring(DNS_AS_STR(token), target));
+
+ /*
+ * Fill in HIT len.
+ */
+ len = (unsigned char *)isc_buffer_used(target) - start;
+ if (len > 0xffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint8_tobuffer((uint32_t)len, &hit_len));
+
+ /*
+ * Public key (base64).
+ */
+ start = isc_buffer_used(target);
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ false));
+ RETTOK(isc_base64_decodestring(DNS_AS_STR(token), target));
+
+ /*
+ * Fill in KEY len.
+ */
+ len = (unsigned char *)isc_buffer_used(target) - start;
+ if (len > 0xffffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint16_tobuffer((uint32_t)len, &key_len));
+
+ if (origin == NULL)
+ origin = dns_rootname;
+
+ /*
+ * Rendezvous Servers.
+ */
+ dns_name_init(&name, NULL);
+ do {
+ RETERR(isc_lex_getmastertoken(lexer, &token,
+ isc_tokentype_string,
+ true));
+ if (token.type != isc_tokentype_string)
+ break;
+ buffer_fromregion(&buffer, &token.value.as_region);
+ RETTOK(dns_name_fromtext(&name, &buffer, origin, options,
+ target));
+ } while (1);
+
+ /*
+ * Let upper layer handle eol/eof.
+ */
+ isc_lex_ungettoken(lexer, &token);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_hip(ARGS_TOTEXT) {
+ isc_region_t region;
+ dns_name_t name;
+ unsigned int length, key_len, hit_len;
+ unsigned char algorithm;
+ char buf[sizeof("225 ")];
+
+ REQUIRE(rdata->type == dns_rdatatype_hip);
+ REQUIRE(rdata->length != 0);
+
+ dns_rdata_toregion(rdata, &region);
+
+ hit_len = uint8_fromregion(&region);
+ isc_region_consume(&region, 1);
+
+ algorithm = uint8_fromregion(&region);
+ isc_region_consume(&region, 1);
+
+ key_len = uint16_fromregion(&region);
+ isc_region_consume(&region, 2);
+
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext("( ", target));
+
+ /*
+ * Algorithm
+ */
+ snprintf(buf, sizeof(buf), "%u ", algorithm);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * HIT.
+ */
+ INSIST(hit_len < region.length);
+ length = region.length;
+ region.length = hit_len;
+ RETERR(isc_hex_totext(&region, 1, "", target));
+ region.length = length - hit_len;
+ RETERR(str_totext(tctx->linebreak, target));
+
+ /*
+ * Public KEY.
+ */
+ INSIST(key_len <= region.length);
+ length = region.length;
+ region.length = key_len;
+ RETERR(isc_base64_totext(&region, 1, "", target));
+ region.length = length - key_len;
+ RETERR(str_totext(tctx->linebreak, target));
+
+ /*
+ * Rendezvous Servers.
+ */
+ dns_name_init(&name, NULL);
+ while (region.length > 0) {
+ dns_name_fromregion(&name, &region);
+
+ RETERR(dns_name_totext(&name, false, target));
+ isc_region_consume(&region, name.length);
+ if (region.length > 0)
+ RETERR(str_totext(tctx->linebreak, target));
+ }
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext(" )", target));
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+fromwire_hip(ARGS_FROMWIRE) {
+ isc_region_t region, rr;
+ dns_name_t name;
+ uint8_t hit_len;
+ uint16_t key_len;
+
+ REQUIRE(type == dns_rdatatype_hip);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ isc_buffer_activeregion(source, &region);
+ if (region.length < 4U)
+ RETERR(DNS_R_FORMERR);
+
+ rr = region;
+ hit_len = uint8_fromregion(&region);
+ if (hit_len == 0)
+ RETERR(DNS_R_FORMERR);
+ isc_region_consume(&region, 2); /* hit length + algorithm */
+ key_len = uint16_fromregion(&region);
+ if (key_len == 0)
+ RETERR(DNS_R_FORMERR);
+ isc_region_consume(&region, 2);
+ if (region.length < (unsigned) (hit_len + key_len))
+ RETERR(DNS_R_FORMERR);
+
+ RETERR(mem_tobuffer(target, rr.base, 4 + hit_len + key_len));
+ isc_buffer_forward(source, 4 + hit_len + key_len);
+
+ dns_decompress_setmethods(dctx, DNS_COMPRESS_NONE);
+ while (isc_buffer_activelength(source) > 0) {
+ dns_name_init(&name, NULL);
+ RETERR(dns_name_fromwire(&name, source, dctx, options, target));
+ }
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+towire_hip(ARGS_TOWIRE) {
+ isc_region_t region;
+
+ REQUIRE(rdata->type == dns_rdatatype_hip);
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(cctx);
+
+ dns_rdata_toregion(rdata, &region);
+ return (mem_tobuffer(target, region.base, region.length));
+}
+
+static inline int
+compare_hip(ARGS_COMPARE) {
+ isc_region_t region1;
+ isc_region_t region2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == dns_rdatatype_hip);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_rdata_toregion(rdata1, &region1);
+ dns_rdata_toregion(rdata2, &region2);
+ return (isc_region_compare(&region1, &region2));
+}
+
+static inline isc_result_t
+fromstruct_hip(ARGS_FROMSTRUCT) {
+ dns_rdata_hip_t *hip = source;
+ dns_rdata_hip_t myhip;
+ isc_result_t result;
+
+ REQUIRE(type == dns_rdatatype_hip);
+ REQUIRE(source != NULL);
+ REQUIRE(hip->common.rdtype == type);
+ REQUIRE(hip->common.rdclass == rdclass);
+ REQUIRE(hip->hit_len > 0 && hip->hit != NULL);
+ REQUIRE(hip->key_len > 0 && hip->key != NULL);
+ REQUIRE((hip->servers == NULL && hip->servers_len == 0) ||
+ (hip->servers != NULL && hip->servers_len != 0));
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ RETERR(uint8_tobuffer(hip->hit_len, target));
+ RETERR(uint8_tobuffer(hip->algorithm, target));
+ RETERR(uint16_tobuffer(hip->key_len, target));
+ RETERR(mem_tobuffer(target, hip->hit, hip->hit_len));
+ RETERR(mem_tobuffer(target, hip->key, hip->key_len));
+
+ myhip = *hip;
+ for (result = dns_rdata_hip_first(&myhip);
+ result == ISC_R_SUCCESS;
+ result = dns_rdata_hip_next(&myhip))
+ /* empty */;
+
+ return(mem_tobuffer(target, hip->servers, hip->servers_len));
+}
+
+static inline isc_result_t
+tostruct_hip(ARGS_TOSTRUCT) {
+ isc_region_t region;
+ dns_rdata_hip_t *hip = target;
+
+ REQUIRE(rdata->type == dns_rdatatype_hip);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ hip->common.rdclass = rdata->rdclass;
+ hip->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&hip->common, link);
+
+ dns_rdata_toregion(rdata, &region);
+
+ hip->hit_len = uint8_fromregion(&region);
+ isc_region_consume(&region, 1);
+
+ hip->algorithm = uint8_fromregion(&region);
+ isc_region_consume(&region, 1);
+
+ hip->key_len = uint16_fromregion(&region);
+ isc_region_consume(&region, 2);
+
+ hip->hit = hip->key = hip->servers = NULL;
+
+ hip->hit = mem_maybedup(mctx, region.base, hip->hit_len);
+ if (hip->hit == NULL)
+ goto cleanup;
+ isc_region_consume(&region, hip->hit_len);
+
+ INSIST(hip->key_len <= region.length);
+
+ hip->key = mem_maybedup(mctx, region.base, hip->key_len);
+ if (hip->key == NULL)
+ goto cleanup;
+ isc_region_consume(&region, hip->key_len);
+
+ hip->servers_len = region.length;
+ if (hip->servers_len != 0) {
+ hip->servers = mem_maybedup(mctx, region.base, region.length);
+ if (hip->servers == NULL)
+ goto cleanup;
+ }
+
+ hip->offset = hip->servers_len;
+ hip->mctx = mctx;
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (hip->hit != NULL)
+ isc_mem_free(mctx, hip->hit);
+ if (hip->key != NULL)
+ isc_mem_free(mctx, hip->key);
+ if (hip->servers != NULL)
+ isc_mem_free(mctx, hip->servers);
+ return (ISC_R_NOMEMORY);
+
+}
+
+static inline void
+freestruct_hip(ARGS_FREESTRUCT) {
+ dns_rdata_hip_t *hip = source;
+
+ REQUIRE(source != NULL);
+
+ if (hip->mctx == NULL)
+ return;
+
+ isc_mem_free(hip->mctx, hip->hit);
+ isc_mem_free(hip->mctx, hip->key);
+ if (hip->servers != NULL)
+ isc_mem_free(hip->mctx, hip->servers);
+ hip->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_hip(ARGS_ADDLDATA) {
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ REQUIRE(rdata->type == dns_rdatatype_hip);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_hip(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == dns_rdatatype_hip);
+
+ dns_rdata_toregion(rdata, &r);
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_hip(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_hip);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_hip(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_hip);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+isc_result_t
+dns_rdata_hip_first(dns_rdata_hip_t *hip) {
+ if (hip->servers_len == 0)
+ return (ISC_R_NOMORE);
+ hip->offset = 0;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_rdata_hip_next(dns_rdata_hip_t *hip) {
+ isc_region_t region;
+ dns_name_t name;
+
+ if (hip->offset >= hip->servers_len)
+ return (ISC_R_NOMORE);
+
+ region.base = hip->servers + hip->offset;
+ region.length = hip->servers_len - hip->offset;
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &region);
+ hip->offset += name.length;
+ INSIST(hip->offset <= hip->servers_len);
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_rdata_hip_current(dns_rdata_hip_t *hip, dns_name_t *name) {
+ isc_region_t region;
+
+ REQUIRE(hip->offset < hip->servers_len);
+
+ region.base = hip->servers + hip->offset;
+ region.length = hip->servers_len - hip->offset;
+ dns_name_fromregion(name, &region);
+
+ INSIST(name->length + hip->offset <= hip->servers_len);
+}
+
+static inline int
+casecompare_hip(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+ dns_name_t name1;
+ dns_name_t name2;
+ int order;
+ uint8_t hit_len;
+ uint16_t key_len;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == dns_rdatatype_hip);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+
+ INSIST(r1.length > 4);
+ INSIST(r2.length > 4);
+ order = memcmp(r1.base, r2.base, 4);
+ if (order != 0)
+ return (order);
+
+ hit_len = uint8_fromregion(&r1);
+ isc_region_consume(&r1, 2); /* hit length + algorithm */
+ key_len = uint16_fromregion(&r1);
+ isc_region_consume(&r1, 2); /* key length */
+ isc_region_consume(&r2, 4);
+
+ INSIST(r1.length >= (unsigned) (hit_len + key_len));
+ INSIST(r2.length >= (unsigned) (hit_len + key_len));
+ order = memcmp(r1.base, r2.base, hit_len + key_len);
+ if (order != 0)
+ return (order);
+ isc_region_consume(&r1, hit_len + key_len);
+ isc_region_consume(&r2, hit_len + key_len);
+
+ dns_name_init(&name1, NULL);
+ dns_name_init(&name2, NULL);
+ while (r1.length != 0 && r2.length != 0) {
+ 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));
+}
+
+#endif /* RDATA_GENERIC_HIP_5_C */
diff --git a/lib/dns/rdata/generic/hip_55.h b/lib/dns/rdata/generic/hip_55.h
new file mode 100644
index 0000000..12c195c
--- /dev/null
+++ b/lib/dns/rdata/generic/hip_55.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#ifndef GENERIC_HIP_5_H
+#define GENERIC_HIP_5_H 1
+
+/* RFC 5205 */
+
+typedef struct dns_rdata_hip {
+ dns_rdatacommon_t common;
+ isc_mem_t * mctx;
+ unsigned char * hit;
+ unsigned char * key;
+ unsigned char * servers;
+ uint8_t algorithm;
+ uint8_t hit_len;
+ uint16_t key_len;
+ uint16_t servers_len;
+ /* Private */
+ uint16_t offset;
+} dns_rdata_hip_t;
+
+isc_result_t
+dns_rdata_hip_first(dns_rdata_hip_t *);
+
+isc_result_t
+dns_rdata_hip_next(dns_rdata_hip_t *);
+
+void
+dns_rdata_hip_current(dns_rdata_hip_t *, dns_name_t *);
+
+#endif /* GENERIC_HIP_5_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..f68f0b8
--- /dev/null
+++ b/lib/dns/rdata/generic/ipseckey_45.c
@@ -0,0 +1,496 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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 == dns_rdatatype_ipseckey);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ /*
+ * Precedence.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ 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,
+ 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,
+ 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,
+ 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);
+ memmove(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);
+ memmove(region.base, addr6, 16);
+ isc_buffer_add(target, 16);
+ break;
+
+ case 3:
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ if (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;
+ char buf[sizeof("255 ")];
+ unsigned short num;
+ unsigned short gateway;
+
+ REQUIRE(rdata->type == dns_rdatatype_ipseckey);
+ REQUIRE(rdata->length >= 3);
+
+ dns_name_init(&name, NULL);
+
+ if (rdata->data[1] > 3U)
+ return (ISC_R_NOTIMPLEMENTED);
+
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext("( ", target));
+
+ /*
+ * Precedence.
+ */
+ dns_rdata_toregion(rdata, &region);
+ num = uint8_fromregion(&region);
+ isc_region_consume(&region, 1);
+ snprintf(buf, sizeof(buf), "%u ", num);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * Gateway type.
+ */
+ gateway = uint8_fromregion(&region);
+ isc_region_consume(&region, 1);
+ snprintf(buf, sizeof(buf), "%u ", gateway);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * Algorithm.
+ */
+ num = uint8_fromregion(&region);
+ isc_region_consume(&region, 1);
+ snprintf(buf, sizeof(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);
+ RETERR(dns_name_totext(&name, false, target));
+ isc_region_consume(&region, name_length(&name));
+ break;
+ }
+
+ /*
+ * Key.
+ */
+ if (region.length > 0U) {
+ RETERR(str_totext(tctx->linebreak, target));
+ if (tctx->width == 0) /* No splitting */
+ RETERR(isc_base64_totext(&region, 60, "", target));
+ else
+ 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 == dns_rdatatype_ipseckey);
+
+ 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);
+ isc_buffer_forward(source, region.length);
+ 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 == dns_rdatatype_ipseckey);
+ 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 == dns_rdatatype_ipseckey);
+ 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;
+ uint32_t n;
+
+ REQUIRE(type == dns_rdatatype_ipseckey);
+ 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;
+ uint32_t n;
+
+ REQUIRE(rdata->type == dns_rdatatype_ipseckey);
+ 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:
+ memmove(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 == dns_rdatatype_ipseckey);
+
+ 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 == dns_rdatatype_ipseckey);
+
+ 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 == dns_rdatatype_ipseckey);
+
+ dns_rdata_toregion(rdata, &region);
+ return ((digest)(arg, &region));
+}
+
+static inline bool
+checkowner_ipseckey(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_ipseckey);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_ipseckey(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_ipseckey);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_ipseckey(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 == dns_rdatatype_ipseckey);
+ REQUIRE(rdata1->length >= 3);
+ REQUIRE(rdata2->length >= 3);
+
+ dns_rdata_toregion(rdata1, &region1);
+ dns_rdata_toregion(rdata2, &region2);
+
+ if (memcmp(region1.base, region2.base, 3) != 0 || region1.base[1] != 3)
+ return (isc_region_compare(&region1, &region2));
+
+ dns_name_init(&name1, NULL);
+ dns_name_init(&name2, NULL);
+
+ isc_region_consume(&region1, 3);
+ isc_region_consume(&region2, 3);
+
+ 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));
+}
+
+#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..e3f5804
--- /dev/null
+++ b/lib/dns/rdata/generic/ipseckey_45.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#ifndef GENERIC_IPSECKEY_45_H
+#define GENERIC_IPSECKEY_45_H 1
+
+typedef struct dns_rdata_ipseckey {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ uint8_t precedence;
+ uint8_t gateway_type;
+ 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;
+ 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..a73cd1f
--- /dev/null
+++ b/lib/dns/rdata/generic/isdn_20.c
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* 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 == dns_rdatatype_isdn);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(callbacks);
+
+ /* ISDN-address */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_qstring,
+ false));
+ RETTOK(txt_fromtext(&token.value.as_textregion, target));
+
+ /* sa: optional */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_qstring,
+ 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 == dns_rdatatype_isdn);
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(tctx);
+
+ dns_rdata_toregion(rdata, &region);
+ RETERR(txt_totext(&region, true, target));
+ if (region.length == 0)
+ return (ISC_R_SUCCESS);
+ RETERR(str_totext(" ", target));
+ return (txt_totext(&region, true, target));
+}
+
+static inline isc_result_t
+fromwire_isdn(ARGS_FROMWIRE) {
+ REQUIRE(type == dns_rdatatype_isdn);
+
+ 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 == dns_rdatatype_isdn);
+ 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 == dns_rdatatype_isdn);
+ 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 == dns_rdatatype_isdn);
+ 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));
+ if (isdn->subaddress == NULL)
+ return (ISC_R_SUCCESS);
+ 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 == dns_rdatatype_isdn);
+ 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);
+
+ if (r.length == 0) {
+ isdn->subaddress_len = 0;
+ isdn->subaddress = NULL;
+ } else {
+ 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 == dns_rdatatype_isdn);
+
+ 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 == dns_rdatatype_isdn);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_isdn(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_isdn);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_isdn(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_isdn);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_isdn(ARGS_COMPARE) {
+ return (compare_isdn(rdata1, rdata2));
+}
+
+#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..48758bb
--- /dev/null
+++ b/lib/dns/rdata/generic/isdn_20.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef GENERIC_ISDN_20_H
+#define GENERIC_ISDN_20_H 1
+
+
+/*!
+ * \brief Per RFC1183 */
+
+typedef struct dns_rdata_isdn {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ char *isdn;
+ char *subaddress;
+ uint8_t isdn_len;
+ 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..6dd2eab
--- /dev/null
+++ b/lib/dns/rdata/generic/key_25.c
@@ -0,0 +1,436 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* 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
+generic_fromtext_key(ARGS_FROMTEXT) {
+ isc_result_t result;
+ isc_token_t token;
+ dns_secalg_t alg;
+ dns_secproto_t proto;
+ dns_keyflags_t flags;
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(callbacks);
+
+ /* flags */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ 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,
+ 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,
+ 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);
+
+ result = isc_base64_tobuffer(lexer, target, -1);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ /* Ensure there's at least enough data to compute a key ID for MD5 */
+ if (alg == DST_ALG_RSAMD5 && isc_buffer_usedlength(target) < 7)
+ return (ISC_R_UNEXPECTEDEND);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+generic_totext_key(ARGS_TOTEXT) {
+ isc_region_t sr;
+ char buf[sizeof("[key id = 64000]")];
+ unsigned int flags;
+ unsigned char algorithm;
+ char algbuf[DNS_NAME_FORMATSIZE];
+ const char *keyinfo;
+ isc_region_t tmpr;
+
+ REQUIRE(rdata->length != 0);
+
+ dns_rdata_toregion(rdata, &sr);
+
+ /* flags */
+ flags = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+ snprintf(buf, sizeof(buf), "%u", flags);
+ RETERR(str_totext(buf, target));
+ RETERR(str_totext(" ", target));
+ if ((flags & DNS_KEYFLAG_KSK) != 0) {
+ if (flags & DNS_KEYFLAG_REVOKE)
+ keyinfo = "revoked KSK";
+ else
+ keyinfo = "KSK";
+ } else
+ keyinfo = "ZSK";
+
+
+ /* protocol */
+ snprintf(buf, sizeof(buf), "%u", sr.base[0]);
+ isc_region_consume(&sr, 1);
+ RETERR(str_totext(buf, target));
+ RETERR(str_totext(" ", target));
+
+ /* algorithm */
+ algorithm = sr.base[0];
+ snprintf(buf, sizeof(buf), "%u", algorithm);
+ isc_region_consume(&sr, 1);
+ RETERR(str_totext(buf, target));
+
+ /* No Key? */
+ if ((flags & 0xc000) == 0xc000)
+ return (ISC_R_SUCCESS);
+
+ if ((tctx->flags & DNS_STYLEFLAG_RRCOMMENT) != 0 &&
+ algorithm == DNS_KEYALG_PRIVATEDNS) {
+ dns_name_t name;
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &sr);
+ dns_name_format(&name, algbuf, sizeof(algbuf));
+ } else {
+ dns_secalg_format((dns_secalg_t) algorithm, algbuf,
+ sizeof(algbuf));
+ }
+
+ /* key */
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext(" (", target));
+ RETERR(str_totext(tctx->linebreak, target));
+
+ if ((tctx->flags & DNS_STYLEFLAG_NOCRYPTO) == 0) {
+ if (tctx->width == 0) /* No splitting */
+ RETERR(isc_base64_totext(&sr, 60, "", target));
+ else
+ RETERR(isc_base64_totext(&sr, tctx->width - 2,
+ tctx->linebreak, target));
+ } else {
+ dns_rdata_toregion(rdata, &tmpr);
+ snprintf(buf, sizeof(buf), "[key id = %u]",
+ dst_region_computeid(&tmpr, algorithm));
+ RETERR(str_totext(buf, target));
+ }
+
+ if ((tctx->flags & DNS_STYLEFLAG_RRCOMMENT) != 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_RRCOMMENT) != 0) {
+
+ if (rdata->type == dns_rdatatype_dnskey ||
+ rdata->type == dns_rdatatype_cdnskey) {
+ RETERR(str_totext(" ; ", target));
+ RETERR(str_totext(keyinfo, target));
+ }
+ RETERR(str_totext("; alg = ", target));
+ RETERR(str_totext(algbuf, target));
+ RETERR(str_totext(" ; key id = ", target));
+ dns_rdata_toregion(rdata, &tmpr);
+ snprintf(buf, sizeof(buf), "%u",
+ dst_region_computeid(&tmpr, algorithm));
+ RETERR(str_totext(buf, target));
+ }
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+generic_fromwire_key(ARGS_FROMWIRE) {
+ unsigned char algorithm;
+ isc_region_t sr;
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(dctx);
+ UNUSED(options);
+
+ isc_buffer_activeregion(source, &sr);
+ if (sr.length < 4)
+ return (ISC_R_UNEXPECTEDEND);
+
+ algorithm = sr.base[3];
+ RETERR(mem_tobuffer(target, sr.base, 4));
+ isc_region_consume(&sr, 4);
+ isc_buffer_forward(source, 4);
+
+ if (algorithm == DNS_KEYALG_PRIVATEDNS) {
+ dns_name_t name;
+ dns_decompress_setmethods(dctx, DNS_COMPRESS_NONE);
+ dns_name_init(&name, NULL);
+ RETERR(dns_name_fromwire(&name, source, dctx, options, target));
+ }
+
+ /*
+ * RSAMD5 computes key ID differently from other
+ * algorithms: we need to ensure there's enough data
+ * present for the computation
+ */
+ if (algorithm == DST_ALG_RSAMD5 && sr.length < 3)
+ return (ISC_R_UNEXPECTEDEND);
+
+ isc_buffer_activeregion(source, &sr);
+ isc_buffer_forward(source, sr.length);
+ return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline isc_result_t
+fromtext_key(ARGS_FROMTEXT) {
+
+ REQUIRE(type == dns_rdatatype_key);
+
+ return (generic_fromtext_key(rdclass, type, lexer, origin,
+ options, target, callbacks));
+}
+
+static inline isc_result_t
+totext_key(ARGS_TOTEXT) {
+
+ REQUIRE(rdata != NULL);
+ REQUIRE(rdata->type == dns_rdatatype_key);
+
+ return (generic_totext_key(rdata, tctx, target));
+}
+
+static inline isc_result_t
+fromwire_key(ARGS_FROMWIRE) {
+
+ REQUIRE(type == dns_rdatatype_key);
+
+ return (generic_fromwire_key(rdclass, type, source, dctx,
+ options, target));
+}
+
+static inline isc_result_t
+towire_key(ARGS_TOWIRE) {
+ isc_region_t sr;
+
+ REQUIRE(rdata != NULL);
+ REQUIRE(rdata->type == dns_rdatatype_key);
+ 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 != NULL);
+ REQUIRE(rdata2 != NULL);
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == dns_rdatatype_key);
+ 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
+generic_fromstruct_key(ARGS_FROMSTRUCT) {
+ dns_rdata_key_t *key = source;
+
+ REQUIRE(key != 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
+generic_tostruct_key(ARGS_TOSTRUCT) {
+ dns_rdata_key_t *key = target;
+ isc_region_t sr;
+
+ REQUIRE(rdata != NULL);
+ REQUIRE(rdata->length != 0);
+
+ REQUIRE(key != NULL);
+ REQUIRE(key->common.rdclass == rdata->rdclass);
+ REQUIRE(key->common.rdtype == rdata->type);
+ REQUIRE(!ISC_LINK_LINKED(&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
+generic_freestruct_key(ARGS_FREESTRUCT) {
+ dns_rdata_key_t *key = (dns_rdata_key_t *) source;
+
+ REQUIRE(key != NULL);
+
+ if (key->mctx == NULL)
+ return;
+
+ if (key->data != NULL)
+ isc_mem_free(key->mctx, key->data);
+ key->mctx = NULL;
+}
+
+static inline isc_result_t
+fromstruct_key(ARGS_FROMSTRUCT) {
+
+ REQUIRE(type == dns_rdatatype_key);
+
+ return (generic_fromstruct_key(rdclass, type, source, target));
+}
+
+static inline isc_result_t
+tostruct_key(ARGS_TOSTRUCT) {
+ dns_rdata_key_t *key = target;
+
+ REQUIRE(key != NULL);
+ REQUIRE(rdata != NULL);
+ REQUIRE(rdata->type == dns_rdatatype_key);
+
+ key->common.rdclass = rdata->rdclass;
+ key->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&key->common, link);
+
+ return (generic_tostruct_key(rdata, target, mctx));
+}
+
+static inline void
+freestruct_key(ARGS_FREESTRUCT) {
+ dns_rdata_key_t *key = (dns_rdata_key_t *) source;
+
+ REQUIRE(key != NULL);
+ REQUIRE(key->common.rdtype == dns_rdatatype_key);
+
+ generic_freestruct_key(source);
+}
+
+static inline isc_result_t
+additionaldata_key(ARGS_ADDLDATA) {
+
+ REQUIRE(rdata != NULL);
+ REQUIRE(rdata->type == dns_rdatatype_key);
+
+ 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 != NULL);
+ REQUIRE(rdata->type == dns_rdatatype_key);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_key(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_key);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_key(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata != NULL);
+ REQUIRE(rdata->type == dns_rdatatype_key);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_key(ARGS_COMPARE) {
+ return (compare_key(rdata1, rdata2));
+}
+
+#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..330363b
--- /dev/null
+++ b/lib/dns/rdata/generic/key_25.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef GENERIC_KEY_25_H
+#define GENERIC_KEY_25_H 1
+
+
+/*!
+ * \brief Per RFC2535 */
+
+typedef struct dns_rdata_key {
+ dns_rdatacommon_t common;
+ isc_mem_t * mctx;
+ uint16_t flags;
+ uint8_t protocol;
+ uint8_t algorithm;
+ uint16_t datalen;
+ unsigned char * data;
+} dns_rdata_key_t;
+
+
+#endif /* GENERIC_KEY_25_H */
diff --git a/lib/dns/rdata/generic/keydata_65533.c b/lib/dns/rdata/generic/keydata_65533.c
new file mode 100644
index 0000000..06b56c1
--- /dev/null
+++ b/lib/dns/rdata/generic/keydata_65533.c
@@ -0,0 +1,441 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef GENERIC_KEYDATA_65533_C
+#define GENERIC_KEYDATA_65533_C 1
+
+#include <isc/time.h>
+#include <isc/stdtime.h>
+
+#include <dst/dst.h>
+
+#define RRTYPE_KEYDATA_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_keydata(ARGS_FROMTEXT) {
+ isc_result_t result;
+ isc_token_t token;
+ dns_secalg_t alg;
+ dns_secproto_t proto;
+ dns_keyflags_t flags;
+ uint32_t refresh, addhd, removehd;
+
+ REQUIRE(type == dns_rdatatype_keydata);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(callbacks);
+
+ /* refresh timer */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ false));
+ RETTOK(dns_time32_fromtext(DNS_AS_STR(token), &refresh));
+ RETERR(uint32_tobuffer(refresh, target));
+
+ /* add hold-down */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ false));
+ RETTOK(dns_time32_fromtext(DNS_AS_STR(token), &addhd));
+ RETERR(uint32_tobuffer(addhd, target));
+
+ /* remove hold-down */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ false));
+ RETTOK(dns_time32_fromtext(DNS_AS_STR(token), &removehd));
+ RETERR(uint32_tobuffer(removehd, target));
+
+ /* flags */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ 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,
+ 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,
+ 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);
+
+ result = isc_base64_tobuffer(lexer, target, -1);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ /* Ensure there's at least enough data to compute a key ID for MD5 */
+ if (alg == DST_ALG_RSAMD5 && isc_buffer_usedlength(target) < 19)
+ return (ISC_R_UNEXPECTEDEND);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_keydata(ARGS_TOTEXT) {
+ isc_region_t sr;
+ char buf[sizeof("64000")];
+ unsigned int flags;
+ unsigned char algorithm;
+ unsigned long refresh, add, deltime;
+ char algbuf[DNS_NAME_FORMATSIZE];
+ const char *keyinfo;
+
+ REQUIRE(rdata->type == dns_rdatatype_keydata);
+
+ if ((tctx->flags & DNS_STYLEFLAG_KEYDATA) == 0 || rdata->length < 16)
+ return (unknown_totext(rdata, tctx, target));
+
+ dns_rdata_toregion(rdata, &sr);
+
+ /* refresh timer */
+ refresh = uint32_fromregion(&sr);
+ isc_region_consume(&sr, 4);
+ RETERR(dns_time32_totext(refresh, target));
+ RETERR(str_totext(" ", target));
+
+ /* add hold-down */
+ add = uint32_fromregion(&sr);
+ isc_region_consume(&sr, 4);
+ RETERR(dns_time32_totext(add, target));
+ RETERR(str_totext(" ", target));
+
+ /* remove hold-down */
+ deltime = uint32_fromregion(&sr);
+ isc_region_consume(&sr, 4);
+ RETERR(dns_time32_totext(deltime, target));
+ RETERR(str_totext(" ", target));
+
+ /* flags */
+ flags = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+ snprintf(buf, sizeof(buf), "%u", flags);
+ RETERR(str_totext(buf, target));
+ RETERR(str_totext(" ", target));
+ if ((flags & DNS_KEYFLAG_KSK) != 0) {
+ if (flags & DNS_KEYFLAG_REVOKE)
+ keyinfo = "revoked KSK";
+ else
+ keyinfo = "KSK";
+ } else
+ keyinfo = "ZSK";
+
+ /* protocol */
+ snprintf(buf, sizeof(buf), "%u", sr.base[0]);
+ isc_region_consume(&sr, 1);
+ RETERR(str_totext(buf, target));
+ RETERR(str_totext(" ", target));
+
+ /* algorithm */
+ algorithm = sr.base[0];
+ snprintf(buf, sizeof(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));
+ if (tctx->width == 0) /* No splitting */
+ RETERR(isc_base64_totext(&sr, 60, "", target));
+ else
+ RETERR(isc_base64_totext(&sr, tctx->width - 2,
+ tctx->linebreak, target));
+
+ if ((tctx->flags & DNS_STYLEFLAG_RRCOMMENT) != 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_RRCOMMENT) != 0) {
+ isc_region_t tmpr;
+ char rbuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
+ char abuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
+ char dbuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
+ isc_time_t t;
+
+ RETERR(str_totext(" ; ", target));
+ RETERR(str_totext(keyinfo, target));
+ dns_secalg_format((dns_secalg_t) algorithm, algbuf,
+ sizeof(algbuf));
+ RETERR(str_totext("; alg = ", target));
+ RETERR(str_totext(algbuf, target));
+ RETERR(str_totext("; key id = ", target));
+ dns_rdata_toregion(rdata, &tmpr);
+ /* Skip over refresh, addhd, and removehd */
+ isc_region_consume(&tmpr, 12);
+ snprintf(buf, sizeof(buf), "%u",
+ dst_region_computeid(&tmpr, algorithm));
+ RETERR(str_totext(buf, target));
+
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) {
+ isc_stdtime_t now;
+
+ isc_stdtime_get(&now);
+
+ RETERR(str_totext(tctx->linebreak, target));
+ RETERR(str_totext("; next refresh: ", target));
+ isc_time_set(&t, refresh, 0);
+ isc_time_formathttptimestamp(&t, rbuf, sizeof(rbuf));
+ RETERR(str_totext(rbuf, target));
+
+ if (add == 0U) {
+ RETERR(str_totext(tctx->linebreak, target));
+ RETERR(str_totext("; no trust", target));
+ } else {
+ RETERR(str_totext(tctx->linebreak, target));
+ if (add < now) {
+ RETERR(str_totext("; trusted since: ",
+ target));
+ } else {
+ RETERR(str_totext("; trust pending: ",
+ target));
+ }
+ isc_time_set(&t, add, 0);
+ isc_time_formathttptimestamp(&t, abuf,
+ sizeof(abuf));
+ RETERR(str_totext(abuf, target));
+ }
+
+ if (deltime != 0U) {
+ RETERR(str_totext(tctx->linebreak, target));
+ RETERR(str_totext("; removal pending: ",
+ target));
+ isc_time_set(&t, deltime, 0);
+ isc_time_formathttptimestamp(&t, dbuf,
+ sizeof(dbuf));
+ RETERR(str_totext(dbuf, target));
+ }
+ }
+
+ }
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+fromwire_keydata(ARGS_FROMWIRE) {
+ isc_region_t sr;
+
+ REQUIRE(type == dns_rdatatype_keydata);
+
+ 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_keydata(ARGS_TOWIRE) {
+ isc_region_t sr;
+
+ REQUIRE(rdata->type == dns_rdatatype_keydata);
+
+ UNUSED(cctx);
+
+ dns_rdata_toregion(rdata, &sr);
+ return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline int
+compare_keydata(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == dns_rdatatype_keydata);
+
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+ return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+fromstruct_keydata(ARGS_FROMSTRUCT) {
+ dns_rdata_keydata_t *keydata = source;
+
+ REQUIRE(type == dns_rdatatype_keydata);
+ REQUIRE(source != NULL);
+ REQUIRE(keydata->common.rdtype == type);
+ REQUIRE(keydata->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ /* Refresh timer */
+ RETERR(uint32_tobuffer(keydata->refresh, target));
+
+ /* Add hold-down */
+ RETERR(uint32_tobuffer(keydata->addhd, target));
+
+ /* Remove hold-down */
+ RETERR(uint32_tobuffer(keydata->removehd, target));
+
+ /* Flags */
+ RETERR(uint16_tobuffer(keydata->flags, target));
+
+ /* Protocol */
+ RETERR(uint8_tobuffer(keydata->protocol, target));
+
+ /* Algorithm */
+ RETERR(uint8_tobuffer(keydata->algorithm, target));
+
+ /* Data */
+ return (mem_tobuffer(target, keydata->data, keydata->datalen));
+}
+
+static inline isc_result_t
+tostruct_keydata(ARGS_TOSTRUCT) {
+ dns_rdata_keydata_t *keydata = target;
+ isc_region_t sr;
+
+ REQUIRE(rdata->type == dns_rdatatype_keydata);
+ REQUIRE(target != NULL);
+
+ keydata->common.rdclass = rdata->rdclass;
+ keydata->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&keydata->common, link);
+
+ dns_rdata_toregion(rdata, &sr);
+
+ /* Refresh timer */
+ if (sr.length < 4)
+ return (ISC_R_UNEXPECTEDEND);
+ keydata->refresh = uint32_fromregion(&sr);
+ isc_region_consume(&sr, 4);
+
+ /* Add hold-down */
+ if (sr.length < 4)
+ return (ISC_R_UNEXPECTEDEND);
+ keydata->addhd = uint32_fromregion(&sr);
+ isc_region_consume(&sr, 4);
+
+ /* Remove hold-down */
+ if (sr.length < 4)
+ return (ISC_R_UNEXPECTEDEND);
+ keydata->removehd = uint32_fromregion(&sr);
+ isc_region_consume(&sr, 4);
+
+ /* Flags */
+ if (sr.length < 2)
+ return (ISC_R_UNEXPECTEDEND);
+ keydata->flags = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+
+ /* Protocol */
+ if (sr.length < 1)
+ return (ISC_R_UNEXPECTEDEND);
+ keydata->protocol = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+
+ /* Algorithm */
+ if (sr.length < 1)
+ return (ISC_R_UNEXPECTEDEND);
+ keydata->algorithm = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+
+ /* Data */
+ keydata->datalen = sr.length;
+ keydata->data = mem_maybedup(mctx, sr.base, keydata->datalen);
+ if (keydata->data == NULL)
+ return (ISC_R_NOMEMORY);
+
+ keydata->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_keydata(ARGS_FREESTRUCT) {
+ dns_rdata_keydata_t *keydata = (dns_rdata_keydata_t *) source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(keydata->common.rdtype == dns_rdatatype_keydata);
+
+ if (keydata->mctx == NULL)
+ return;
+
+ if (keydata->data != NULL)
+ isc_mem_free(keydata->mctx, keydata->data);
+ keydata->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_keydata(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == dns_rdatatype_keydata);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_keydata(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == dns_rdatatype_keydata);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_keydata(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_keydata);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_keydata(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_keydata);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_keydata(ARGS_COMPARE) {
+ return (compare_keydata(rdata1, rdata2));
+}
+
+#endif /* GENERIC_KEYDATA_65533_C */
diff --git a/lib/dns/rdata/generic/keydata_65533.h b/lib/dns/rdata/generic/keydata_65533.h
new file mode 100644
index 0000000..40061e1
--- /dev/null
+++ b/lib/dns/rdata/generic/keydata_65533.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef GENERIC_KEYDATA_65533_H
+#define GENERIC_KEYDATA_65533_H 1
+
+
+typedef struct dns_rdata_keydata {
+ dns_rdatacommon_t common;
+ isc_mem_t * mctx;
+ uint32_t refresh; /* Timer for refreshing data */
+ uint32_t addhd; /* Hold-down timer for adding */
+ uint32_t removehd; /* Hold-down timer for removing */
+ uint16_t flags; /* Copy of DNSKEY_48 */
+ uint8_t protocol;
+ uint8_t algorithm;
+ uint16_t datalen;
+ unsigned char * data;
+} dns_rdata_keydata_t;
+
+#endif /* GENERIC_KEYDATA_65533_H */
diff --git a/lib/dns/rdata/generic/l32_105.c b/lib/dns/rdata/generic/l32_105.c
new file mode 100644
index 0000000..7d92cc2
--- /dev/null
+++ b/lib/dns/rdata/generic/l32_105.c
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef RDATA_GENERIC_L32_105_C
+#define RDATA_GENERIC_L32_105_C
+
+#include <string.h>
+
+#include <isc/net.h>
+
+#define RRTYPE_L32_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_l32(ARGS_FROMTEXT) {
+ isc_token_t token;
+ struct in_addr addr;
+ isc_region_t region;
+
+ REQUIRE(type == dns_rdatatype_l32);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(callbacks);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ 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,
+ 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);
+ memmove(region.base, &addr, 4);
+ isc_buffer_add(target, 4);
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_l32(ARGS_TOTEXT) {
+ isc_region_t region;
+ char buf[sizeof("65000")];
+ unsigned short num;
+
+ REQUIRE(rdata->type == dns_rdatatype_l32);
+ REQUIRE(rdata->length == 6);
+
+ UNUSED(tctx);
+
+ dns_rdata_toregion(rdata, &region);
+ num = uint16_fromregion(&region);
+ isc_region_consume(&region, 2);
+ snprintf(buf, sizeof(buf), "%u", num);
+ RETERR(str_totext(buf, target));
+
+ RETERR(str_totext(" ", target));
+
+ return (inet_totext(AF_INET, &region, target));
+}
+
+static inline isc_result_t
+fromwire_l32(ARGS_FROMWIRE) {
+ isc_region_t sregion;
+
+ REQUIRE(type == dns_rdatatype_l32);
+
+ UNUSED(type);
+ UNUSED(options);
+ UNUSED(rdclass);
+ UNUSED(dctx);
+
+ isc_buffer_activeregion(source, &sregion);
+ if (sregion.length != 6)
+ return (DNS_R_FORMERR);
+ isc_buffer_forward(source, sregion.length);
+ return (mem_tobuffer(target, sregion.base, sregion.length));
+}
+
+static inline isc_result_t
+towire_l32(ARGS_TOWIRE) {
+
+ REQUIRE(rdata->type == dns_rdatatype_l32);
+ REQUIRE(rdata->length == 6);
+
+ UNUSED(cctx);
+
+ return (mem_tobuffer(target, rdata->data, rdata->length));
+}
+
+static inline int
+compare_l32(ARGS_COMPARE) {
+ isc_region_t region1;
+ isc_region_t region2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == dns_rdatatype_l32);
+ REQUIRE(rdata1->length == 6);
+ REQUIRE(rdata2->length == 6);
+
+ dns_rdata_toregion(rdata1, &region1);
+ dns_rdata_toregion(rdata2, &region2);
+ return (isc_region_compare(&region1, &region2));
+}
+
+static inline isc_result_t
+fromstruct_l32(ARGS_FROMSTRUCT) {
+ dns_rdata_l32_t *l32 = source;
+ uint32_t n;
+
+ REQUIRE(type == dns_rdatatype_l32);
+ REQUIRE(source != NULL);
+ REQUIRE(l32->common.rdtype == type);
+ REQUIRE(l32->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ RETERR(uint16_tobuffer(l32->pref, target));
+ n = ntohl(l32->l32.s_addr);
+ return (uint32_tobuffer(n, target));
+}
+
+static inline isc_result_t
+tostruct_l32(ARGS_TOSTRUCT) {
+ isc_region_t region;
+ dns_rdata_l32_t *l32 = target;
+ uint32_t n;
+
+ REQUIRE(rdata->type == dns_rdatatype_l32);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length == 6);
+
+ UNUSED(mctx);
+
+ l32->common.rdclass = rdata->rdclass;
+ l32->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&l32->common, link);
+
+ dns_rdata_toregion(rdata, &region);
+ l32->pref = uint16_fromregion(&region);
+ n = uint32_fromregion(&region);
+ l32->l32.s_addr = htonl(n);
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_l32(ARGS_FREESTRUCT) {
+ dns_rdata_l32_t *l32 = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(l32->common.rdtype == dns_rdatatype_l32);
+
+ return;
+}
+
+static inline isc_result_t
+additionaldata_l32(ARGS_ADDLDATA) {
+
+ REQUIRE(rdata->type == dns_rdatatype_l32);
+ REQUIRE(rdata->length == 6);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_l32(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == dns_rdatatype_l32);
+ REQUIRE(rdata->length == 6);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_l32(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_l32);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_l32(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_l32);
+ REQUIRE(rdata->length == 6);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_l32(ARGS_COMPARE) {
+ return (compare_l32(rdata1, rdata2));
+}
+
+#endif /* RDATA_GENERIC_L32_105_C */
diff --git a/lib/dns/rdata/generic/l32_105.h b/lib/dns/rdata/generic/l32_105.h
new file mode 100644
index 0000000..3fca0f2
--- /dev/null
+++ b/lib/dns/rdata/generic/l32_105.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* */
+#ifndef GENERIC_L32_105_H
+#define GENERIC_L32_105_H 1
+
+typedef struct dns_rdata_l32 {
+ dns_rdatacommon_t common;
+ uint16_t pref;
+ struct in_addr l32;
+} dns_rdata_l32_t;
+
+#endif /* GENERIC_L32_105_H */
diff --git a/lib/dns/rdata/generic/l64_106.c b/lib/dns/rdata/generic/l64_106.c
new file mode 100644
index 0000000..277c6a9
--- /dev/null
+++ b/lib/dns/rdata/generic/l64_106.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef RDATA_GENERIC_L64_106_C
+#define RDATA_GENERIC_L64_106_C
+
+#include <string.h>
+
+#include <isc/net.h>
+
+#define RRTYPE_L64_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_l64(ARGS_FROMTEXT) {
+ isc_token_t token;
+ unsigned char locator[NS_LOCATORSZ];
+
+ REQUIRE(type == dns_rdatatype_l64);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(callbacks);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ 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,
+ false));
+
+ if (locator_pton(DNS_AS_STR(token), locator) != 1)
+ RETTOK(DNS_R_SYNTAX);
+ return (mem_tobuffer(target, locator, NS_LOCATORSZ));
+}
+
+static inline isc_result_t
+totext_l64(ARGS_TOTEXT) {
+ isc_region_t region;
+ char buf[sizeof("xxxx:xxxx:xxxx:xxxx")];
+ unsigned short num;
+
+ REQUIRE(rdata->type == dns_rdatatype_l64);
+ REQUIRE(rdata->length == 10);
+
+ UNUSED(tctx);
+
+ dns_rdata_toregion(rdata, &region);
+ num = uint16_fromregion(&region);
+ isc_region_consume(&region, 2);
+ snprintf(buf, sizeof(buf), "%u", num);
+ RETERR(str_totext(buf, target));
+
+ RETERR(str_totext(" ", target));
+
+ snprintf(buf, sizeof(buf), "%x:%x:%x:%x",
+ region.base[0]<<8 | region.base[1],
+ region.base[2]<<8 | region.base[3],
+ region.base[4]<<8 | region.base[5],
+ region.base[6]<<8 | region.base[7]);
+ return (str_totext(buf, target));
+}
+
+static inline isc_result_t
+fromwire_l64(ARGS_FROMWIRE) {
+ isc_region_t sregion;
+
+ REQUIRE(type == dns_rdatatype_l64);
+
+ UNUSED(type);
+ UNUSED(options);
+ UNUSED(rdclass);
+ UNUSED(dctx);
+
+ isc_buffer_activeregion(source, &sregion);
+ if (sregion.length != 10)
+ return (DNS_R_FORMERR);
+ isc_buffer_forward(source, sregion.length);
+ return (mem_tobuffer(target, sregion.base, sregion.length));
+}
+
+static inline isc_result_t
+towire_l64(ARGS_TOWIRE) {
+
+ REQUIRE(rdata->type == dns_rdatatype_l64);
+ REQUIRE(rdata->length == 10);
+
+ UNUSED(cctx);
+
+ return (mem_tobuffer(target, rdata->data, rdata->length));
+}
+
+static inline int
+compare_l64(ARGS_COMPARE) {
+ isc_region_t region1;
+ isc_region_t region2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == dns_rdatatype_l64);
+ REQUIRE(rdata1->length == 10);
+ REQUIRE(rdata2->length == 10);
+
+ dns_rdata_toregion(rdata1, &region1);
+ dns_rdata_toregion(rdata2, &region2);
+ return (isc_region_compare(&region1, &region2));
+}
+
+static inline isc_result_t
+fromstruct_l64(ARGS_FROMSTRUCT) {
+ dns_rdata_l64_t *l64 = source;
+
+ REQUIRE(type == dns_rdatatype_l64);
+ REQUIRE(source != NULL);
+ REQUIRE(l64->common.rdtype == type);
+ REQUIRE(l64->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ RETERR(uint16_tobuffer(l64->pref, target));
+ return (mem_tobuffer(target, l64->l64, sizeof(l64->l64)));
+}
+
+static inline isc_result_t
+tostruct_l64(ARGS_TOSTRUCT) {
+ isc_region_t region;
+ dns_rdata_l64_t *l64 = target;
+
+ REQUIRE(rdata->type == dns_rdatatype_l64);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length == 10);
+
+ UNUSED(mctx);
+
+ l64->common.rdclass = rdata->rdclass;
+ l64->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&l64->common, link);
+
+ dns_rdata_toregion(rdata, &region);
+ l64->pref = uint16_fromregion(&region);
+ memmove(l64->l64, region.base, region.length);
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_l64(ARGS_FREESTRUCT) {
+ dns_rdata_l64_t *l64 = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(l64->common.rdtype == dns_rdatatype_l64);
+
+ return;
+}
+
+static inline isc_result_t
+additionaldata_l64(ARGS_ADDLDATA) {
+
+ REQUIRE(rdata->type == dns_rdatatype_l64);
+ REQUIRE(rdata->length == 10);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_l64(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == dns_rdatatype_l64);
+ REQUIRE(rdata->length == 10);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_l64(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_l64);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_l64(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_l64);
+ REQUIRE(rdata->length == 10);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_l64(ARGS_COMPARE) {
+ return (compare_l64(rdata1, rdata2));
+}
+
+#endif /* RDATA_GENERIC_L64_106_C */
diff --git a/lib/dns/rdata/generic/l64_106.h b/lib/dns/rdata/generic/l64_106.h
new file mode 100644
index 0000000..968a138
--- /dev/null
+++ b/lib/dns/rdata/generic/l64_106.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* */
+#ifndef GENERIC_L64_106_H
+#define GENERIC_L64_106_H 1
+
+typedef struct dns_rdata_l64 {
+ dns_rdatacommon_t common;
+ uint16_t pref;
+ unsigned char l64[8];
+} dns_rdata_l64_t;
+
+#endif /* GENERIC_L64_106_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..50c00ff
--- /dev/null
+++ b/lib/dns/rdata/generic/loc_29.c
@@ -0,0 +1,811 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* 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;
+ bool east = false;
+ bool north = 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 == dns_rdatatype_loc);
+
+ 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,
+ 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,
+ false));
+ if (strcasecmp(DNS_AS_STR(token), "N") == 0)
+ north = 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,
+ false));
+ if (strcasecmp(DNS_AS_STR(token), "N") == 0)
+ north = 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,
+ false));
+ if (strcasecmp(DNS_AS_STR(token), "N") == 0)
+ north = 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,
+ 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,
+ false));
+ if (strcasecmp(DNS_AS_STR(token), "E") == 0)
+ east = 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,
+ false));
+ if (strcasecmp(DNS_AS_STR(token), "E") == 0)
+ east = 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,
+ false));
+ if (strcasecmp(DNS_AS_STR(token), "E") == 0)
+ east = 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,
+ 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,
+ 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,
+ 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,
+ 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;
+ bool north;
+ bool east;
+ bool 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 == dns_rdatatype_loc);
+ REQUIRE(rdata->length != 0);
+
+ dns_rdata_toregion(rdata, &sr);
+
+ if (sr.base[0] != 0)
+ return (ISC_R_NOTIMPLEMENTED);
+
+ REQUIRE(rdata->length == 16);
+
+ size = sr.base[1];
+ INSIST((size&0x0f) < 10 && (size>>4) < 10);
+ if ((size&0x0f)> 1) {
+ snprintf(sbuf, sizeof(sbuf),
+ "%lum", (size>>4) * poweroften[(size&0x0f)-2]);
+ } else {
+ snprintf(sbuf, sizeof(sbuf),
+ "0.%02lum", (size>>4) * poweroften[(size&0x0f)]);
+ }
+ hp = sr.base[2];
+ INSIST((hp&0x0f) < 10 && (hp>>4) < 10);
+ if ((hp&0x0f)> 1) {
+ snprintf(hbuf, sizeof(hbuf),
+ "%lum", (hp>>4) * poweroften[(hp&0x0f)-2]);
+ } else {
+ snprintf(hbuf, sizeof(hbuf),
+ "0.%02lum", (hp>>4) * poweroften[(hp&0x0f)]);
+ }
+ vp = sr.base[3];
+ INSIST((vp&0x0f) < 10 && (vp>>4) < 10);
+ if ((vp&0x0f)> 1) {
+ snprintf(vbuf, sizeof(vbuf),
+ "%lum", (vp>>4) * poweroften[(vp&0x0f)-2]);
+ } else {
+ snprintf(vbuf, sizeof(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 = true;
+ latitude -= 0x80000000;
+ } else {
+ north = 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;
+ INSIST(latitude <= 90U);
+
+ longitude = uint32_fromregion(&sr);
+ isc_region_consume(&sr, 4);
+ if (longitude >= 0x80000000) {
+ east = true;
+ longitude -= 0x80000000;
+ } else {
+ east = 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;
+ INSIST(longitude <= 180U);
+
+ altitude = uint32_fromregion(&sr);
+ isc_region_consume(&sr, 4);
+ if (altitude < 10000000U) {
+ below = true;
+ altitude = 10000000 - altitude;
+ } else {
+ below =false;
+ altitude -= 10000000;
+ }
+
+ snprintf(buf, sizeof(buf),
+ "%d %d %d.%03d %s %d %d %d.%03d %s %s%lu.%02lum %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 == dns_rdatatype_loc);
+
+ 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) {
+ /* Treat as unknown. */
+ isc_buffer_forward(source, sr.length);
+ return (mem_tobuffer(target, sr.base, sr.length));
+ }
+ 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);
+
+ /*
+ * Altitude.
+ * 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 == dns_rdatatype_loc);
+ 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 == dns_rdatatype_loc);
+ 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;
+ uint8_t c;
+
+ REQUIRE(type == dns_rdatatype_loc);
+ 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;
+ uint8_t version;
+
+ REQUIRE(rdata->type == dns_rdatatype_loc);
+ 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 == dns_rdatatype_loc);
+
+ UNUSED(source);
+ UNUSED(loc);
+}
+
+static inline isc_result_t
+additionaldata_loc(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == dns_rdatatype_loc);
+
+ 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 == dns_rdatatype_loc);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_loc(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_loc);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_loc(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_loc);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_loc(ARGS_COMPARE) {
+ return (compare_loc(rdata1, rdata2));
+}
+
+#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..c879581
--- /dev/null
+++ b/lib/dns/rdata/generic/loc_29.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef GENERIC_LOC_29_H
+#define GENERIC_LOC_29_H 1
+
+
+/*!
+ * \brief Per RFC1876 */
+
+typedef struct dns_rdata_loc_0 {
+ uint8_t version; /* must be first and zero */
+ uint8_t size;
+ uint8_t horizontal;
+ uint8_t vertical;
+ uint32_t latitude;
+ uint32_t longitude;
+ 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/lp_107.c b/lib/dns/rdata/generic/lp_107.c
new file mode 100644
index 0000000..49bc799
--- /dev/null
+++ b/lib/dns/rdata/generic/lp_107.c
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef RDATA_GENERIC_LP_107_C
+#define RDATA_GENERIC_LP_107_C
+
+#include <string.h>
+
+#include <isc/net.h>
+
+#define RRTYPE_LP_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_lp(ARGS_FROMTEXT) {
+ isc_token_t token;
+ dns_name_t name;
+ isc_buffer_t buffer;
+
+ REQUIRE(type == dns_rdatatype_lp);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ 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,
+ false));
+
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ if (origin == NULL)
+ origin = dns_rootname;
+ return (dns_name_fromtext(&name, &buffer, origin, options, target));
+}
+
+static inline isc_result_t
+totext_lp(ARGS_TOTEXT) {
+ isc_region_t region;
+ dns_name_t name;
+ dns_name_t prefix;
+ bool sub;
+ char buf[sizeof("64000")];
+ unsigned short num;
+
+ REQUIRE(rdata->type == dns_rdatatype_lp);
+ 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);
+ snprintf(buf, sizeof(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_lp(ARGS_FROMWIRE) {
+ dns_name_t name;
+ isc_region_t sregion;
+
+ REQUIRE(type == dns_rdatatype_lp);
+
+ 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_lp(ARGS_TOWIRE) {
+
+ REQUIRE(rdata->type == dns_rdatatype_lp);
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(cctx);
+
+ return (mem_tobuffer(target, rdata->data, rdata->length));
+}
+
+static inline int
+compare_lp(ARGS_COMPARE) {
+ isc_region_t region1;
+ isc_region_t region2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == dns_rdatatype_lp);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_rdata_toregion(rdata1, &region1);
+ dns_rdata_toregion(rdata2, &region2);
+
+ return (isc_region_compare(&region1, &region2));
+}
+
+static inline isc_result_t
+fromstruct_lp(ARGS_FROMSTRUCT) {
+ dns_rdata_lp_t *lp = source;
+ isc_region_t region;
+
+ REQUIRE(type == dns_rdatatype_lp);
+ REQUIRE(source != NULL);
+ REQUIRE(lp->common.rdtype == type);
+ REQUIRE(lp->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ RETERR(uint16_tobuffer(lp->pref, target));
+ dns_name_toregion(&lp->lp, &region);
+ return (isc_buffer_copyregion(target, &region));
+}
+
+static inline isc_result_t
+tostruct_lp(ARGS_TOSTRUCT) {
+ isc_region_t region;
+ dns_rdata_lp_t *lp = target;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == dns_rdatatype_lp);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ lp->common.rdclass = rdata->rdclass;
+ lp->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&lp->common, link);
+
+ dns_name_init(&name, NULL);
+ dns_rdata_toregion(rdata, &region);
+ lp->pref = uint16_fromregion(&region);
+ isc_region_consume(&region, 2);
+ dns_name_fromregion(&name, &region);
+ dns_name_init(&lp->lp, NULL);
+ RETERR(name_duporclone(&name, mctx, &lp->lp));
+ lp->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_lp(ARGS_FREESTRUCT) {
+ dns_rdata_lp_t *lp = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(lp->common.rdtype == dns_rdatatype_lp);
+
+ if (lp->mctx == NULL)
+ return;
+
+ dns_name_free(&lp->lp, lp->mctx);
+ lp->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_lp(ARGS_ADDLDATA) {
+ dns_name_t name;
+ dns_offsets_t offsets;
+ isc_region_t region;
+ isc_result_t result;
+
+ REQUIRE(rdata->type == dns_rdatatype_lp);
+
+ 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_l32);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ return ((add)(arg, &name, dns_rdatatype_l64));
+}
+
+static inline isc_result_t
+digest_lp(ARGS_DIGEST) {
+ isc_region_t region;
+
+ REQUIRE(rdata->type == dns_rdatatype_lp);
+
+ dns_rdata_toregion(rdata, &region);
+ return ((digest)(arg, &region));
+}
+
+static inline bool
+checkowner_lp(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_lp);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(name);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_lp(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_lp);
+
+ UNUSED(bad);
+ UNUSED(owner);
+
+ return (true);
+}
+
+static inline int
+casecompare_lp(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 == dns_rdatatype_lp);
+ 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));
+}
+
+#endif /* RDATA_GENERIC_LP_107_C */
diff --git a/lib/dns/rdata/generic/lp_107.h b/lib/dns/rdata/generic/lp_107.h
new file mode 100644
index 0000000..4ccffac
--- /dev/null
+++ b/lib/dns/rdata/generic/lp_107.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* */
+#ifndef GENERIC_LP_107_H
+#define GENERIC_LP_107_H 1
+
+typedef struct dns_rdata_lp {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ uint16_t pref;
+ dns_name_t lp;
+} dns_rdata_lp_t;
+
+#endif /* GENERIC_LP_107_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..299f89d
--- /dev/null
+++ b/lib/dns/rdata/generic/mb_7.c
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#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 == dns_rdatatype_mb);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ false));
+
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ if (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;
+ bool sub;
+
+ REQUIRE(rdata->type == dns_rdatatype_mb);
+ 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 == dns_rdatatype_mb);
+
+ 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 == dns_rdatatype_mb);
+ 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 == dns_rdatatype_mb);
+ 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 == dns_rdatatype_mb);
+ 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 == dns_rdatatype_mb);
+ 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 == dns_rdatatype_mb);
+
+ 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 == dns_rdatatype_mb);
+
+ dns_rdata_toregion(rdata, &r);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &r);
+
+ return (dns_name_digest(&name, digest, arg));
+}
+
+static inline bool
+checkowner_mb(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_mb);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (dns_name_ismailbox(name));
+}
+
+static inline bool
+checknames_mb(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_mb);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_mb(ARGS_COMPARE) {
+ return (compare_mb(rdata1, rdata2));
+}
+
+#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..59f4115
--- /dev/null
+++ b/lib/dns/rdata/generic/mb_7.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* */
+#ifndef GENERIC_MB_7_H
+#define GENERIC_MB_7_H 1
+
+
+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..9eefe69
--- /dev/null
+++ b/lib/dns/rdata/generic/md_3.c
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#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 == dns_rdatatype_md);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ false));
+
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ if (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;
+ bool sub;
+
+ REQUIRE(rdata->type == dns_rdatatype_md);
+ 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 == dns_rdatatype_md);
+
+ 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 == dns_rdatatype_md);
+ 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 == dns_rdatatype_md);
+ 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 == dns_rdatatype_md);
+ 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 == dns_rdatatype_md);
+ 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 == dns_rdatatype_md);
+
+ 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 == dns_rdatatype_md);
+
+ 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 == dns_rdatatype_md);
+
+ dns_rdata_toregion(rdata, &r);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &r);
+
+ return (dns_name_digest(&name, digest, arg));
+}
+
+static inline bool
+checkowner_md(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_md);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_md(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_md);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_md(ARGS_COMPARE) {
+ return (compare_md(rdata1, rdata2));
+}
+
+#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..396c3c9
--- /dev/null
+++ b/lib/dns/rdata/generic/md_3.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* */
+#ifndef GENERIC_MD_3_H
+#define GENERIC_MD_3_H 1
+
+
+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..5965981
--- /dev/null
+++ b/lib/dns/rdata/generic/mf_4.c
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#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 == dns_rdatatype_mf);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ false));
+
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ if (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;
+ bool sub;
+
+ REQUIRE(rdata->type == dns_rdatatype_mf);
+ 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 == dns_rdatatype_mf);
+
+ 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 == dns_rdatatype_mf);
+ 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 == dns_rdatatype_mf);
+ 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 == dns_rdatatype_mf);
+ 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 == dns_rdatatype_mf);
+ 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 == dns_rdatatype_mf);
+
+ 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 == dns_rdatatype_mf);
+
+ 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 == dns_rdatatype_mf);
+
+ dns_rdata_toregion(rdata, &r);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &r);
+
+ return (dns_name_digest(&name, digest, arg));
+}
+
+static inline bool
+checkowner_mf(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_mf);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_mf(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_mf);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_mf(ARGS_COMPARE) {
+ return (compare_mf(rdata1, rdata2));
+}
+
+#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..d882155
--- /dev/null
+++ b/lib/dns/rdata/generic/mf_4.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* */
+#ifndef GENERIC_MF_4_H
+#define GENERIC_MF_4_H 1
+
+
+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..45a38ce
--- /dev/null
+++ b/lib/dns/rdata/generic/mg_8.c
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#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 == dns_rdatatype_mg);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ false));
+
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ if (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;
+ bool sub;
+
+ REQUIRE(rdata->type == dns_rdatatype_mg);
+ 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 == dns_rdatatype_mg);
+
+ 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 == dns_rdatatype_mg);
+ 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 == dns_rdatatype_mg);
+ 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 == dns_rdatatype_mg);
+ 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 == dns_rdatatype_mg);
+ 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 == dns_rdatatype_mg);
+
+ 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 == dns_rdatatype_mg);
+
+ 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 == dns_rdatatype_mg);
+
+ dns_rdata_toregion(rdata, &r);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &r);
+
+ return (dns_name_digest(&name, digest, arg));
+}
+
+static inline bool
+checkowner_mg(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_mg);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (dns_name_ismailbox(name));
+}
+
+static inline bool
+checknames_mg(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_mg);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_mg(ARGS_COMPARE) {
+ return (compare_mg(rdata1, rdata2));
+}
+
+#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..0e0380e
--- /dev/null
+++ b/lib/dns/rdata/generic/mg_8.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* */
+#ifndef GENERIC_MG_8_H
+#define GENERIC_MG_8_H 1
+
+
+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..23506e2
--- /dev/null
+++ b/lib/dns/rdata/generic/minfo_14.c
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#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;
+ bool ok;
+
+ REQUIRE(type == dns_rdatatype_minfo);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ if (origin == NULL)
+ origin = dns_rootname;
+
+ for (i = 0; i < 2; i++) {
+ RETERR(isc_lex_getmastertoken(lexer, &token,
+ isc_tokentype_string,
+ false));
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ RETTOK(dns_name_fromtext(&name, &buffer, origin,
+ options, target));
+ ok = 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;
+ bool sub;
+
+ REQUIRE(rdata->type == dns_rdatatype_minfo);
+ 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 == dns_rdatatype_minfo);
+
+ 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 == dns_rdatatype_minfo);
+ 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 == dns_rdatatype_minfo);
+ 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 == dns_rdatatype_minfo);
+ 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 == dns_rdatatype_minfo);
+ 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 == dns_rdatatype_minfo);
+
+ 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 == dns_rdatatype_minfo);
+
+ 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 == dns_rdatatype_minfo);
+
+ 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 bool
+checkowner_minfo(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_minfo);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_minfo(ARGS_CHECKNAMES) {
+ isc_region_t region;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == dns_rdatatype_minfo);
+
+ 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 (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 (false);
+ }
+ return (true);
+}
+
+static inline int
+casecompare_minfo(ARGS_COMPARE) {
+ return (compare_minfo(rdata1, rdata2));
+}
+
+#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..10080fb
--- /dev/null
+++ b/lib/dns/rdata/generic/minfo_14.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* */
+#ifndef GENERIC_MINFO_14_H
+#define GENERIC_MINFO_14_H 1
+
+
+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..edea7b1
--- /dev/null
+++ b/lib/dns/rdata/generic/mr_9.c
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#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 == dns_rdatatype_mr);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ false));
+
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ if (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;
+ bool sub;
+
+ REQUIRE(rdata->type == dns_rdatatype_mr);
+ 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 == dns_rdatatype_mr);
+
+ 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 == dns_rdatatype_mr);
+ 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 == dns_rdatatype_mr);
+ 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 == dns_rdatatype_mr);
+ 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 == dns_rdatatype_mr);
+ 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 == dns_rdatatype_mr);
+
+ 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 == dns_rdatatype_mr);
+
+ 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 == dns_rdatatype_mr);
+
+ dns_rdata_toregion(rdata, &r);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &r);
+
+ return (dns_name_digest(&name, digest, arg));
+}
+
+static inline bool
+checkowner_mr(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_mr);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_mr(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_mr);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_mr(ARGS_COMPARE) {
+ return (compare_mr(rdata1, rdata2));
+}
+
+#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..888a82f
--- /dev/null
+++ b/lib/dns/rdata/generic/mr_9.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* */
+#ifndef GENERIC_MR_9_H
+#define GENERIC_MR_9_H 1
+
+
+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..c333740
--- /dev/null
+++ b/lib/dns/rdata/generic/mx_15.c
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef RDATA_GENERIC_MX_15_C
+#define RDATA_GENERIC_MX_15_C
+
+#include <string.h>
+
+#include <isc/net.h>
+
+#include <dns/fixedname.h>
+
+#define RRTYPE_MX_ATTRIBUTES (0)
+
+static bool
+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 (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 (false);
+
+ return (true);
+}
+
+static inline isc_result_t
+fromtext_mx(ARGS_FROMTEXT) {
+ isc_token_t token;
+ dns_name_t name;
+ isc_buffer_t buffer;
+ bool ok;
+
+ REQUIRE(type == dns_rdatatype_mx);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ 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,
+ false));
+
+ ok = 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);
+ if (origin == NULL)
+ origin = dns_rootname;
+ RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target));
+ ok = true;
+ if ((options & DNS_RDATA_CHECKNAMES) != 0)
+ ok = dns_name_ishostname(&name, 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;
+ bool sub;
+ char buf[sizeof("64000")];
+ unsigned short num;
+
+ REQUIRE(rdata->type == dns_rdatatype_mx);
+ 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);
+ snprintf(buf, sizeof(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 == dns_rdatatype_mx);
+
+ 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 == dns_rdatatype_mx);
+ 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 == dns_rdatatype_mx);
+ 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 == dns_rdatatype_mx);
+ 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 == dns_rdatatype_mx);
+ 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 == dns_rdatatype_mx);
+
+ if (mx->mctx == NULL)
+ return;
+
+ dns_name_free(&mx->mx, mx->mctx);
+ mx->mctx = NULL;
+}
+
+static unsigned char port25_offset[] = { 0, 3 };
+static unsigned char port25_ndata[] = "\003_25\004_tcp";
+static dns_name_t port25 =
+ DNS_NAME_INITNONABSOLUTE(port25_ndata, port25_offset);
+
+static inline isc_result_t
+additionaldata_mx(ARGS_ADDLDATA) {
+ isc_result_t result;
+ dns_fixedname_t fixed;
+ dns_name_t name;
+ dns_offsets_t offsets;
+ isc_region_t region;
+
+ REQUIRE(rdata->type == dns_rdatatype_mx);
+
+ dns_name_init(&name, offsets);
+ dns_rdata_toregion(rdata, &region);
+ isc_region_consume(&region, 2);
+ dns_name_fromregion(&name, &region);
+
+ if (dns_name_equal(&name, dns_rootname))
+ return (ISC_R_SUCCESS);
+
+ result = (add)(arg, &name, dns_rdatatype_a);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ dns_fixedname_init(&fixed);
+ result = dns_name_concatenate(&port25, &name,
+ dns_fixedname_name(&fixed), NULL);
+ if (result != ISC_R_SUCCESS)
+ return (ISC_R_SUCCESS);
+
+ return ((add)(arg, dns_fixedname_name(&fixed), dns_rdatatype_tlsa));
+}
+
+static inline isc_result_t
+digest_mx(ARGS_DIGEST) {
+ isc_region_t r1, r2;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == dns_rdatatype_mx);
+
+ 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 bool
+checkowner_mx(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_mx);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ return (dns_name_ishostname(name, wildcard));
+}
+
+static inline bool
+checknames_mx(ARGS_CHECKNAMES) {
+ isc_region_t region;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == dns_rdatatype_mx);
+
+ 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, false)) {
+ if (bad != NULL)
+ dns_name_clone(&name, bad);
+ return (false);
+ }
+ return (true);
+}
+
+static inline int
+casecompare_mx(ARGS_COMPARE) {
+ return (compare_mx(rdata1, rdata2));
+}
+
+#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..ed96beb
--- /dev/null
+++ b/lib/dns/rdata/generic/mx_15.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* */
+#ifndef GENERIC_MX_15_H
+#define GENERIC_MX_15_H 1
+
+
+typedef struct dns_rdata_mx {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ uint16_t pref;
+ dns_name_t mx;
+} dns_rdata_mx_t;
+
+#endif /* GENERIC_MX_15_H */
diff --git a/lib/dns/rdata/generic/naptr_35.c b/lib/dns/rdata/generic/naptr_35.c
new file mode 100644
index 0000000..80c8f16
--- /dev/null
+++ b/lib/dns/rdata/generic/naptr_35.c
@@ -0,0 +1,662 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* RFC2915 */
+
+#ifndef RDATA_GENERIC_NAPTR_35_C
+#define RDATA_GENERIC_NAPTR_35_C
+
+#define RRTYPE_NAPTR_ATTRIBUTES (0)
+
+#include <isc/regex.h>
+
+/*
+ * Check the wire format of the Regexp field.
+ * Don't allow embeded NUL's.
+ */
+static inline isc_result_t
+txt_valid_regex(const unsigned char *txt) {
+ unsigned int nsub = 0;
+ char regex[256];
+ char *cp;
+ bool flags = false;
+ bool replace = false;
+ unsigned char c;
+ unsigned char delim;
+ unsigned int len;
+ int n;
+
+ len = *txt++;
+ if (len == 0U)
+ return (ISC_R_SUCCESS);
+
+ delim = *txt++;
+ len--;
+
+ /*
+ * Digits, backslash and flags can't be delimiters.
+ */
+ switch (delim) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ case '\\': case 'i': case 0:
+ return (DNS_R_SYNTAX);
+ }
+
+ cp = regex;
+ while (len-- > 0) {
+ c = *txt++;
+ if (c == 0)
+ return (DNS_R_SYNTAX);
+ if (c == delim && !replace) {
+ replace = true;
+ continue;
+ } else if (c == delim && !flags) {
+ flags = true;
+ continue;
+ } else if (c == delim)
+ return (DNS_R_SYNTAX);
+ /*
+ * Flags are not escaped.
+ */
+ if (flags) {
+ switch (c) {
+ case 'i':
+ continue;
+ default:
+ return (DNS_R_SYNTAX);
+ }
+ }
+ if (!replace)
+ *cp++ = c;
+ if (c == '\\') {
+ if (len == 0)
+ return (DNS_R_SYNTAX);
+ c = *txt++;
+ if (c == 0)
+ return (DNS_R_SYNTAX);
+ len--;
+ if (replace)
+ switch (c) {
+ case '0': return (DNS_R_SYNTAX);
+ case '1': if (nsub < 1) nsub = 1; break;
+ case '2': if (nsub < 2) nsub = 2; break;
+ case '3': if (nsub < 3) nsub = 3; break;
+ case '4': if (nsub < 4) nsub = 4; break;
+ case '5': if (nsub < 5) nsub = 5; break;
+ case '6': if (nsub < 6) nsub = 6; break;
+ case '7': if (nsub < 7) nsub = 7; break;
+ case '8': if (nsub < 8) nsub = 8; break;
+ case '9': if (nsub < 9) nsub = 9; break;
+ }
+ if (!replace)
+ *cp++ = c;
+ }
+ }
+ if (!flags)
+ return (DNS_R_SYNTAX);
+ *cp = '\0';
+ n = isc_regex_validate(regex);
+ if (n < 0 || nsub > (unsigned int)n)
+ return (DNS_R_SYNTAX);
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+fromtext_naptr(ARGS_FROMTEXT) {
+ isc_token_t token;
+ dns_name_t name;
+ isc_buffer_t buffer;
+ unsigned char *regex;
+
+ REQUIRE(type == dns_rdatatype_naptr);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ /*
+ * Order.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ 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,
+ 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,
+ false));
+ RETTOK(txt_fromtext(&token.value.as_textregion, target));
+
+ /*
+ * Service.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_qstring,
+ false));
+ RETTOK(txt_fromtext(&token.value.as_textregion, target));
+
+ /*
+ * Regexp.
+ */
+ regex = isc_buffer_used(target);
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_qstring,
+ false));
+ RETTOK(txt_fromtext(&token.value.as_textregion, target));
+ RETTOK(txt_valid_regex(regex));
+
+ /*
+ * Replacement.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ false));
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ if (origin == NULL)
+ origin = dns_rootname;
+ RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target));
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_naptr(ARGS_TOTEXT) {
+ isc_region_t region;
+ dns_name_t name;
+ dns_name_t prefix;
+ bool sub;
+ char buf[sizeof("64000")];
+ unsigned short num;
+
+ REQUIRE(rdata->type == dns_rdatatype_naptr);
+ 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);
+ snprintf(buf, sizeof(buf), "%u", num);
+ RETERR(str_totext(buf, target));
+ RETERR(str_totext(" ", target));
+
+ /*
+ * Preference.
+ */
+ num = uint16_fromregion(&region);
+ isc_region_consume(&region, 2);
+ snprintf(buf, sizeof(buf), "%u", num);
+ RETERR(str_totext(buf, target));
+ RETERR(str_totext(" ", target));
+
+ /*
+ * Flags.
+ */
+ RETERR(txt_totext(&region, true, target));
+ RETERR(str_totext(" ", target));
+
+ /*
+ * Service.
+ */
+ RETERR(txt_totext(&region, true, target));
+ RETERR(str_totext(" ", target));
+
+ /*
+ * Regexp.
+ */
+ RETERR(txt_totext(&region, true, 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_naptr(ARGS_FROMWIRE) {
+ dns_name_t name;
+ isc_region_t sr;
+ unsigned char *regex;
+
+ REQUIRE(type == dns_rdatatype_naptr);
+
+ 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.
+ */
+ regex = isc_buffer_used(target);
+ RETERR(txt_fromwire(source, target));
+ RETERR(txt_valid_regex(regex));
+
+ /*
+ * Replacement.
+ */
+ return (dns_name_fromwire(&name, source, dctx, options, target));
+}
+
+static inline isc_result_t
+towire_naptr(ARGS_TOWIRE) {
+ dns_name_t name;
+ dns_offsets_t offsets;
+ isc_region_t sr;
+
+ REQUIRE(rdata->type == dns_rdatatype_naptr);
+ 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_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 == dns_rdatatype_naptr);
+ 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_naptr(ARGS_FROMSTRUCT) {
+ dns_rdata_naptr_t *naptr = source;
+ isc_region_t region;
+
+ REQUIRE(type == dns_rdatatype_naptr);
+ 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_naptr(ARGS_TOSTRUCT) {
+ dns_rdata_naptr_t *naptr = target;
+ isc_region_t r;
+ isc_result_t result;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == dns_rdatatype_naptr);
+ 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_naptr(ARGS_FREESTRUCT) {
+ dns_rdata_naptr_t *naptr = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(naptr->common.rdtype == dns_rdatatype_naptr);
+
+ 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_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 == dns_rdatatype_naptr);
+
+ /*
+ * 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_naptr(ARGS_DIGEST) {
+ isc_region_t r1, r2;
+ unsigned int length, n;
+ isc_result_t result;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == dns_rdatatype_naptr);
+
+ 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 bool
+checkowner_naptr(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_naptr);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_naptr(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_naptr);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_naptr(ARGS_COMPARE) {
+ return (compare_naptr(rdata1, rdata2));
+}
+
+#endif /* RDATA_GENERIC_NAPTR_35_C */
diff --git a/lib/dns/rdata/generic/naptr_35.h b/lib/dns/rdata/generic/naptr_35.h
new file mode 100644
index 0000000..e72b609
--- /dev/null
+++ b/lib/dns/rdata/generic/naptr_35.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef GENERIC_NAPTR_35_H
+#define GENERIC_NAPTR_35_H 1
+
+
+/*!
+ * \brief Per RFC2915 */
+
+typedef struct dns_rdata_naptr {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ uint16_t order;
+ uint16_t preference;
+ char *flags;
+ uint8_t flags_len;
+ char *service;
+ uint8_t service_len;
+ char *regexp;
+ uint8_t regexp_len;
+ dns_name_t replacement;
+} dns_rdata_naptr_t;
+
+#endif /* GENERIC_NAPTR_35_H */
diff --git a/lib/dns/rdata/generic/nid_104.c b/lib/dns/rdata/generic/nid_104.c
new file mode 100644
index 0000000..fee61fd
--- /dev/null
+++ b/lib/dns/rdata/generic/nid_104.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef RDATA_GENERIC_NID_104_C
+#define RDATA_GENERIC_NID_104_C
+
+#include <string.h>
+
+#include <isc/net.h>
+
+#define RRTYPE_NID_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_nid(ARGS_FROMTEXT) {
+ isc_token_t token;
+ unsigned char locator[NS_LOCATORSZ];
+
+ REQUIRE(type == dns_rdatatype_nid);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(callbacks);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ 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,
+ false));
+
+ if (locator_pton(DNS_AS_STR(token), locator) != 1)
+ RETTOK(DNS_R_SYNTAX);
+ return (mem_tobuffer(target, locator, NS_LOCATORSZ));
+}
+
+static inline isc_result_t
+totext_nid(ARGS_TOTEXT) {
+ isc_region_t region;
+ char buf[sizeof("xxxx:xxxx:xxxx:xxxx")];
+ unsigned short num;
+
+ REQUIRE(rdata->type == dns_rdatatype_nid);
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(tctx);
+
+ dns_rdata_toregion(rdata, &region);
+ num = uint16_fromregion(&region);
+ isc_region_consume(&region, 2);
+ snprintf(buf, sizeof(buf), "%u", num);
+ RETERR(str_totext(buf, target));
+
+ RETERR(str_totext(" ", target));
+
+ snprintf(buf, sizeof(buf), "%x:%x:%x:%x",
+ region.base[0]<<8 | region.base[1],
+ region.base[2]<<8 | region.base[3],
+ region.base[4]<<8 | region.base[5],
+ region.base[6]<<8 | region.base[7]);
+ return (str_totext(buf, target));
+}
+
+static inline isc_result_t
+fromwire_nid(ARGS_FROMWIRE) {
+ isc_region_t sregion;
+
+ REQUIRE(type == dns_rdatatype_nid);
+
+ UNUSED(type);
+ UNUSED(options);
+ UNUSED(rdclass);
+ UNUSED(dctx);
+
+ isc_buffer_activeregion(source, &sregion);
+ if (sregion.length != 10)
+ return (DNS_R_FORMERR);
+ isc_buffer_forward(source, sregion.length);
+ return (mem_tobuffer(target, sregion.base, sregion.length));
+}
+
+static inline isc_result_t
+towire_nid(ARGS_TOWIRE) {
+
+ REQUIRE(rdata->type == dns_rdatatype_nid);
+ REQUIRE(rdata->length == 10);
+
+ UNUSED(cctx);
+
+ return (mem_tobuffer(target, rdata->data, rdata->length));
+}
+
+static inline int
+compare_nid(ARGS_COMPARE) {
+ isc_region_t region1;
+ isc_region_t region2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == dns_rdatatype_nid);
+ REQUIRE(rdata1->length == 10);
+ REQUIRE(rdata2->length == 10);
+
+ dns_rdata_toregion(rdata1, &region1);
+ dns_rdata_toregion(rdata2, &region2);
+ return (isc_region_compare(&region1, &region2));
+}
+
+static inline isc_result_t
+fromstruct_nid(ARGS_FROMSTRUCT) {
+ dns_rdata_nid_t *nid = source;
+
+ REQUIRE(type == dns_rdatatype_nid);
+ REQUIRE(source != NULL);
+ REQUIRE(nid->common.rdtype == type);
+ REQUIRE(nid->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ RETERR(uint16_tobuffer(nid->pref, target));
+ return (mem_tobuffer(target, nid->nid, sizeof(nid->nid)));
+}
+
+static inline isc_result_t
+tostruct_nid(ARGS_TOSTRUCT) {
+ isc_region_t region;
+ dns_rdata_nid_t *nid = target;
+
+ REQUIRE(rdata->type == dns_rdatatype_nid);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length == 10);
+
+ UNUSED(mctx);
+
+ nid->common.rdclass = rdata->rdclass;
+ nid->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&nid->common, link);
+
+ dns_rdata_toregion(rdata, &region);
+ nid->pref = uint16_fromregion(&region);
+ memmove(nid->nid, region.base, region.length);
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_nid(ARGS_FREESTRUCT) {
+ dns_rdata_nid_t *nid = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(nid->common.rdtype == dns_rdatatype_nid);
+
+ return;
+}
+
+static inline isc_result_t
+additionaldata_nid(ARGS_ADDLDATA) {
+
+ REQUIRE(rdata->type == dns_rdatatype_nid);
+ REQUIRE(rdata->length == 10);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_nid(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == dns_rdatatype_nid);
+ REQUIRE(rdata->length == 10);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_nid(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_nid);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_nid(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_nid);
+ REQUIRE(rdata->length == 10);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_nid(ARGS_COMPARE) {
+ return (compare_nid(rdata1, rdata2));
+}
+
+#endif /* RDATA_GENERIC_NID_104_C */
diff --git a/lib/dns/rdata/generic/nid_104.h b/lib/dns/rdata/generic/nid_104.h
new file mode 100644
index 0000000..9362e2b
--- /dev/null
+++ b/lib/dns/rdata/generic/nid_104.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* */
+#ifndef GENERIC_NID_104_H
+#define GENERIC_NID_104_H 1
+
+typedef struct dns_rdata_nid {
+ dns_rdatacommon_t common;
+ uint16_t pref;
+ unsigned char nid[8];
+} dns_rdata_nid_t;
+
+#endif /* GENERIC_NID_104_H */
diff --git a/lib/dns/rdata/generic/ninfo_56.c b/lib/dns/rdata/generic/ninfo_56.c
new file mode 100644
index 0000000..fe8957a
--- /dev/null
+++ b/lib/dns/rdata/generic/ninfo_56.c
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef RDATA_GENERIC_NINFO_56_C
+#define RDATA_GENERIC_NINFO_56_C
+
+#define RRTYPE_NINFO_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_ninfo(ARGS_FROMTEXT) {
+
+ REQUIRE(type == dns_rdatatype_ninfo);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(callbacks);
+
+ return (generic_fromtext_txt(rdclass, type, lexer, origin, options,
+ target, callbacks));
+}
+
+static inline isc_result_t
+totext_ninfo(ARGS_TOTEXT) {
+
+ UNUSED(tctx);
+
+ REQUIRE(rdata->type == dns_rdatatype_ninfo);
+
+ return (generic_totext_txt(rdata, tctx, target));
+}
+
+static inline isc_result_t
+fromwire_ninfo(ARGS_FROMWIRE) {
+
+ REQUIRE(type == dns_rdatatype_ninfo);
+
+ UNUSED(type);
+ UNUSED(dctx);
+ UNUSED(rdclass);
+ UNUSED(options);
+
+ return (generic_fromwire_txt(rdclass, type, source, dctx, options,
+ target));
+}
+
+static inline isc_result_t
+towire_ninfo(ARGS_TOWIRE) {
+
+ REQUIRE(rdata->type == dns_rdatatype_ninfo);
+
+ UNUSED(cctx);
+
+ return (mem_tobuffer(target, rdata->data, rdata->length));
+}
+
+static inline int
+compare_ninfo(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == dns_rdatatype_ninfo);
+
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+ return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+fromstruct_ninfo(ARGS_FROMSTRUCT) {
+
+ REQUIRE(type == dns_rdatatype_ninfo);
+
+ return (generic_fromstruct_txt(rdclass, type, source, target));
+}
+
+static inline isc_result_t
+tostruct_ninfo(ARGS_TOSTRUCT) {
+ dns_rdata_ninfo_t *txt = target;
+
+ REQUIRE(rdata->type == dns_rdatatype_ninfo);
+
+ txt->common.rdclass = rdata->rdclass;
+ txt->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&txt->common, link);
+
+ return (generic_tostruct_txt(rdata, target, mctx));
+}
+
+static inline void
+freestruct_ninfo(ARGS_FREESTRUCT) {
+ dns_rdata_ninfo_t *ninfo = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(ninfo->common.rdtype == dns_rdatatype_ninfo);
+
+ generic_freestruct_txt(source);
+}
+
+static inline isc_result_t
+additionaldata_ninfo(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == dns_rdatatype_ninfo);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_ninfo(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == dns_rdatatype_ninfo);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_ninfo(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_ninfo);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_ninfo(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_ninfo);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_ninfo(ARGS_COMPARE) {
+ return (compare_ninfo(rdata1, rdata2));
+}
+
+isc_result_t
+dns_rdata_ninfo_first(dns_rdata_ninfo_t *ninfo) {
+
+ REQUIRE(ninfo != NULL);
+ REQUIRE(ninfo->common.rdtype == dns_rdatatype_ninfo);
+
+ return (generic_txt_first(ninfo));
+}
+
+isc_result_t
+dns_rdata_ninfo_next(dns_rdata_ninfo_t *ninfo) {
+
+ REQUIRE(ninfo != NULL);
+ REQUIRE(ninfo->common.rdtype == dns_rdatatype_ninfo);
+
+ return (generic_txt_next(ninfo));
+}
+
+isc_result_t
+dns_rdata_ninfo_current(dns_rdata_ninfo_t *ninfo,
+ dns_rdata_ninfo_string_t *string)
+{
+
+ REQUIRE(ninfo != NULL);
+ REQUIRE(ninfo->common.rdtype == dns_rdatatype_ninfo);
+
+ return (generic_txt_current(ninfo, string));
+}
+#endif /* RDATA_GENERIC_NINFO_56_C */
diff --git a/lib/dns/rdata/generic/ninfo_56.h b/lib/dns/rdata/generic/ninfo_56.h
new file mode 100644
index 0000000..68e9212
--- /dev/null
+++ b/lib/dns/rdata/generic/ninfo_56.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* */
+#ifndef GENERIC_NINFO_56_H
+#define GENERIC_NINFO_56_H 1
+
+typedef struct dns_rdata_txt_string dns_rdata_ninfo_string_t;
+
+typedef struct dns_rdata_txt dns_rdata_ninfo_t;
+
+/*
+ * ISC_LANG_BEGINDECLS and ISC_LANG_ENDDECLS are already done
+ * via rdatastructpre.h and rdatastructsuf.h.
+ */
+
+isc_result_t
+dns_rdata_ninfo_first(dns_rdata_ninfo_t *);
+
+isc_result_t
+dns_rdata_ninfo_next(dns_rdata_ninfo_t *);
+
+isc_result_t
+dns_rdata_ninfo_current(dns_rdata_ninfo_t *, dns_rdata_ninfo_string_t *);
+
+#endif /* GENERIC_NINFO_16_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..7f3979f
--- /dev/null
+++ b/lib/dns/rdata/generic/ns_2.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#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;
+ bool ok;
+
+ REQUIRE(type == dns_rdatatype_ns);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token,isc_tokentype_string,
+ false));
+
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ if (origin == NULL)
+ origin = dns_rootname;
+ RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target));
+ ok = true;
+ if ((options & DNS_RDATA_CHECKNAMES) != 0)
+ ok = dns_name_ishostname(&name, 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;
+ bool sub;
+
+ REQUIRE(rdata->type == dns_rdatatype_ns);
+ 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 == dns_rdatatype_ns);
+
+ 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 == dns_rdatatype_ns);
+ 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 == dns_rdatatype_ns);
+ 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 == dns_rdatatype_ns);
+ 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 == dns_rdatatype_ns);
+ 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 == dns_rdatatype_ns);
+
+ 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 == dns_rdatatype_ns);
+
+ dns_rdata_toregion(rdata, &r);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &r);
+
+ return (dns_name_digest(&name, digest, arg));
+}
+
+static inline bool
+checkowner_ns(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_ns);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_ns(ARGS_CHECKNAMES) {
+ isc_region_t region;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == dns_rdatatype_ns);
+
+ UNUSED(owner);
+
+ dns_rdata_toregion(rdata, &region);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &region);
+ if (!dns_name_ishostname(&name, false)) {
+ if (bad != NULL)
+ dns_name_clone(&name, bad);
+ return (false);
+ }
+ return (true);
+}
+
+static inline int
+casecompare_ns(ARGS_COMPARE) {
+ return (compare_ns(rdata1, rdata2));
+}
+
+#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..db9b6c0
--- /dev/null
+++ b/lib/dns/rdata/generic/ns_2.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* */
+#ifndef GENERIC_NS_2_H
+#define GENERIC_NS_2_H 1
+
+
+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..1aedf22
--- /dev/null
+++ b/lib/dns/rdata/generic/nsec3_50.c
@@ -0,0 +1,402 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*
+ * 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 int flags;
+ unsigned char hashalg;
+ isc_buffer_t b;
+ unsigned char buf[256];
+
+ REQUIRE(type == dns_rdatatype_nsec3);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+ UNUSED(origin);
+ UNUSED(options);
+
+ /* Hash. */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ 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,
+ 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,
+ 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,
+ 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,
+ false));
+ isc_buffer_init(&b, buf, sizeof(buf));
+ RETTOK(isc_base32hexnp_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, &buf, isc_buffer_usedlength(&b)));
+
+ return (typemap_fromtext(lexer, target, true));
+}
+
+static inline isc_result_t
+totext_nsec3(ARGS_TOTEXT) {
+ isc_region_t sr;
+ unsigned int i, j;
+ unsigned char hash;
+ unsigned char flags;
+ char buf[sizeof("TYPE65535")];
+ uint32_t iterations;
+
+ REQUIRE(rdata->type == dns_rdatatype_nsec3);
+ REQUIRE(rdata->length != 0);
+
+ dns_rdata_toregion(rdata, &sr);
+
+ /* Hash */
+ hash = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+ snprintf(buf, sizeof(buf), "%u ", hash);
+ RETERR(str_totext(buf, target));
+
+ /* Flags */
+ flags = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+ snprintf(buf, sizeof(buf), "%u ", flags);
+ RETERR(str_totext(buf, target));
+
+ /* Iterations */
+ iterations = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+ snprintf(buf, sizeof(buf), "%u ", iterations);
+ RETERR(str_totext(buf, target));
+
+ /* Salt */
+ 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));
+
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext(" (", target));
+ RETERR(str_totext(tctx->linebreak, target));
+
+ /* Next hash */
+ j = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+ INSIST(j <= sr.length);
+
+ i = sr.length;
+ sr.length = j;
+ RETERR(isc_base32hexnp_totext(&sr, 1, "", target));
+ sr.length = i - j;
+
+ /*
+ * Don't leave a trailing space when there's no typemap present.
+ */
+ if (((tctx->flags & DNS_STYLEFLAG_MULTILINE) == 0) && (sr.length > 0)) {
+ RETERR(str_totext(" ", target));
+ }
+ RETERR(typemap_totext(&sr, tctx, target));
+
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext(" )", target));
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+fromwire_nsec3(ARGS_FROMWIRE) {
+ isc_region_t sr, rr;
+ unsigned int saltlen, hashlen;
+
+ REQUIRE(type == dns_rdatatype_nsec3);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(options);
+ UNUSED(dctx);
+
+ isc_buffer_activeregion(source, &sr);
+ rr = sr;
+
+ /* hash(1), flags(1), iteration(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);
+
+ RETERR(typemap_test(&sr, true));
+
+ 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 == dns_rdatatype_nsec3);
+ 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 == dns_rdatatype_nsec3);
+ 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;
+ isc_region_t region;
+
+ REQUIRE(type == dns_rdatatype_nsec3);
+ 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));
+
+ region.base = nsec3->typebits;
+ region.length = nsec3->len;
+ RETERR(typemap_test(&region, true));
+ 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 == dns_rdatatype_nsec3);
+ 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 == dns_rdatatype_nsec3);
+
+ 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 == dns_rdatatype_nsec3);
+
+ 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 == dns_rdatatype_nsec3);
+
+ dns_rdata_toregion(rdata, &r);
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_nsec3(ARGS_CHECKOWNER) {
+ unsigned char owner[NSEC3_MAX_HASH_LENGTH];
+ isc_buffer_t buffer;
+ dns_label_t label;
+
+ REQUIRE(type == dns_rdatatype_nsec3);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ /*
+ * First label is a base32hex string without padding.
+ */
+ dns_name_getlabel(name, 0, &label);
+ isc_region_consume(&label, 1);
+ isc_buffer_init(&buffer, owner, sizeof(owner));
+ if (isc_base32hexnp_decoderegion(&label, &buffer) == ISC_R_SUCCESS)
+ return (true);
+
+ return (false);
+}
+
+static inline bool
+checknames_nsec3(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_nsec3);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_nsec3(ARGS_COMPARE) {
+ return (compare_nsec3(rdata1, rdata2));
+}
+
+#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..36b9695
--- /dev/null
+++ b/lib/dns/rdata/generic/nsec3_50.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#ifndef GENERIC_NSEC3_50_H
+#define GENERIC_NSEC3_50_H 1
+
+
+/*!
+ * \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;
+ 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
+
+/*%
+ * The following flags are used in the private-type record (implemented in
+ * lib/dns/private.c) which is used to store NSEC3PARAM data during the
+ * time when it is not legal to have an actual NSEC3PARAM record in the
+ * zone. They are defined here because the private-type record uses the
+ * same flags field for the OPTOUT flag above and for the private flags
+ * below. XXX: This should be considered for refactoring.
+ */
+
+/*%
+ * Non-standard, private type 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 containing this flag should never be published,
+ * but if they are, they should be ignored by RFC 5155 compliant
+ * nameservers.
+ */
+#define DNS_NSEC3FLAG_CREATE 0x80U
+
+/*%
+ * Non-standard, private type 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 containing this flag should never be published,
+ * but if they are, they should be ignored by RFC 5155 compliant
+ * nameservers.
+ */
+#define DNS_NSEC3FLAG_REMOVE 0x40U
+
+/*%
+ * Non-standard, private type only.
+ *
+ * When set with the CREATE flag, a corresponding NSEC3 chain will be
+ * created when the zone becomes capable of supporting one (i.e., when it
+ * has a DNSKEY RRset containing at least one NSEC3-capable algorithm).
+ * Without this flag, NSEC3 chain creation would be attempted immediately,
+ * fail, and the private type record would be removed. With it, the NSEC3
+ * parameters are stored until they can be used. When the zone has the
+ * necessary prerequisites for NSEC3, then the INITIAL flag can be cleared,
+ * and the record will be cleaned up normally.
+ *
+ * NSEC3PARAM records containing this flag should never be published, but
+ * if they are, they should be ignored by RFC 5155 compliant nameservers.
+ */
+#define DNS_NSEC3FLAG_INITIAL 0x20U
+
+/*%
+ * Non-standard, private type 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.
+ *
+ * NSEC3PARAM records containing this flag should never be published,
+ * but if they are, they should be ignored by RFC 5155 compliant
+ * nameservers.
+ */
+#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..8a24dee
--- /dev/null
+++ b/lib/dns/rdata/generic/nsec3param_51.c
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*
+ * 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 == dns_rdatatype_nsec3param);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+ UNUSED(origin);
+ UNUSED(options);
+
+ /* Hash. */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ 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,
+ 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,
+ 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,
+ 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 ")];
+ uint32_t iterations;
+
+ REQUIRE(rdata->type == dns_rdatatype_nsec3param);
+ 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);
+
+ snprintf(buf, sizeof(buf), "%u ", hash);
+ RETERR(str_totext(buf, target));
+
+ snprintf(buf, sizeof(buf), "%u ", flags);
+ RETERR(str_totext(buf, target));
+
+ snprintf(buf, sizeof(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 == dns_rdatatype_nsec3param);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(options);
+ UNUSED(dctx);
+
+ isc_buffer_activeregion(source, &sr);
+ rr = sr;
+
+ /* hash(1), flags(1), iterations(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 == dns_rdatatype_nsec3param);
+ 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 == dns_rdatatype_nsec3param);
+ 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 == dns_rdatatype_nsec3param);
+ 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 == dns_rdatatype_nsec3param);
+ 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 == dns_rdatatype_nsec3param);
+
+ 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 == dns_rdatatype_nsec3param);
+
+ 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 == dns_rdatatype_nsec3param);
+
+ dns_rdata_toregion(rdata, &r);
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_nsec3param(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_nsec3param);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_nsec3param(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_nsec3param);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_nsec3param(ARGS_COMPARE) {
+ return (compare_nsec3param(rdata1, rdata2));
+}
+
+#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..b8bc375
--- /dev/null
+++ b/lib/dns/rdata/generic/nsec3param_51.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#ifndef GENERIC_NSEC3PARAM_51_H
+#define GENERIC_NSEC3PARAM_51_H 1
+
+
+/*!
+ * \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..05e575b
--- /dev/null
+++ b/lib/dns/rdata/generic/nsec_47.c
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* 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;
+
+ REQUIRE(type == dns_rdatatype_nsec);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ /*
+ * Next domain.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ false));
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ if (origin == NULL)
+ origin = dns_rootname;
+ RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target));
+
+ return (typemap_fromtext(lexer, target, false));
+}
+
+static inline isc_result_t
+totext_nsec(ARGS_TOTEXT) {
+ isc_region_t sr;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == dns_rdatatype_nsec);
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(tctx);
+
+ dns_name_init(&name, NULL);
+ dns_rdata_toregion(rdata, &sr);
+ dns_name_fromregion(&name, &sr);
+ isc_region_consume(&sr, name_length(&name));
+ RETERR(dns_name_totext(&name, false, target));
+ /*
+ * Don't leave a trailing space when there's no typemap present.
+ */
+ if (sr.length > 0) {
+ RETERR(str_totext(" ", target));
+ }
+ return (typemap_totext(&sr, NULL, target));
+}
+
+static /* inline */ isc_result_t
+fromwire_nsec(ARGS_FROMWIRE) {
+ isc_region_t sr;
+ dns_name_t name;
+
+ REQUIRE(type == dns_rdatatype_nsec);
+
+ 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);
+ RETERR(typemap_test(&sr, false));
+ 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 == dns_rdatatype_nsec);
+ 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 == dns_rdatatype_nsec);
+ 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;
+
+ REQUIRE(type == dns_rdatatype_nsec);
+ 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));
+
+ region.base = nsec->typebits;
+ region.length = nsec->len;
+ RETERR(typemap_test(&region, false));
+ 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 == dns_rdatatype_nsec);
+ 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 == dns_rdatatype_nsec);
+
+ 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 == dns_rdatatype_nsec);
+
+ 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 == dns_rdatatype_nsec);
+
+ dns_rdata_toregion(rdata, &r);
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_nsec(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_nsec);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_nsec(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_nsec);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_nsec(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 == dns_rdatatype_nsec);
+ 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));
+
+ return (isc_region_compare(&region1, &region2));
+}
+#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..b597864
--- /dev/null
+++ b/lib/dns/rdata/generic/nsec_47.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef GENERIC_NSEC_47_H
+#define GENERIC_NSEC_47_H 1
+
+
+/*!
+ * \brief Per RFC 3845 */
+
+typedef struct dns_rdata_nsec {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ dns_name_t next;
+ unsigned char *typebits;
+ 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..624adad
--- /dev/null
+++ b/lib/dns/rdata/generic/null_10.c
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#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 == dns_rdatatype_null);
+
+ 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 == dns_rdatatype_null);
+
+ return (unknown_totext(rdata, tctx, target));
+}
+
+static inline isc_result_t
+fromwire_null(ARGS_FROMWIRE) {
+ isc_region_t sr;
+
+ REQUIRE(type == dns_rdatatype_null);
+
+ 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 == dns_rdatatype_null);
+
+ 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 == dns_rdatatype_null);
+
+ 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 == dns_rdatatype_null);
+ 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 == dns_rdatatype_null);
+ 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 == dns_rdatatype_null);
+
+ 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 == dns_rdatatype_null);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_null(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == dns_rdatatype_null);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_null(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_null);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_null(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_null);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_null(ARGS_COMPARE) {
+ return (compare_null(rdata1, rdata2));
+}
+
+#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..0b01dd8
--- /dev/null
+++ b/lib/dns/rdata/generic/null_10.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* */
+#ifndef GENERIC_NULL_10_H
+#define GENERIC_NULL_10_H 1
+
+
+typedef struct dns_rdata_null {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ 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..91a95b7
--- /dev/null
+++ b/lib/dns/rdata/generic/nxt_30.c
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* 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;
+ bool first = true;
+ long n;
+
+ REQUIRE(type == dns_rdatatype_nxt);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ /*
+ * Next domain.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ false));
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ if (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, 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 = 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;
+ bool sub;
+
+ REQUIRE(rdata->type == dns_rdatatype_nxt);
+ 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")];
+ snprintf(buf, sizeof(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 == dns_rdatatype_nxt);
+
+ 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 == dns_rdatatype_nxt);
+ 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 == dns_rdatatype_nxt);
+ 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 == dns_rdatatype_nxt);
+ 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 == dns_rdatatype_nxt);
+ 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 == dns_rdatatype_nxt);
+
+ 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 == dns_rdatatype_nxt);
+
+ 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 == dns_rdatatype_nxt);
+
+ 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 bool
+checkowner_nxt(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_nxt);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_nxt(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_nxt);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_nxt(ARGS_COMPARE) {
+ return (compare_nxt(rdata1, rdata2));
+}
+#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..63bbc0f
--- /dev/null
+++ b/lib/dns/rdata/generic/nxt_30.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef GENERIC_NXT_30_H
+#define GENERIC_NXT_30_H 1
+
+
+/*!
+ * \brief RFC2535 */
+
+typedef struct dns_rdata_nxt {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ dns_name_t next;
+ unsigned char *typebits;
+ uint16_t len;
+} dns_rdata_nxt_t;
+
+#endif /* GENERIC_NXT_30_H */
diff --git a/lib/dns/rdata/generic/openpgpkey_61.c b/lib/dns/rdata/generic/openpgpkey_61.c
new file mode 100644
index 0000000..52c87d4
--- /dev/null
+++ b/lib/dns/rdata/generic/openpgpkey_61.c
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef RDATA_GENERIC_OPENPGPKEY_61_C
+#define RDATA_GENERIC_OPENPGPKEY_61_C
+
+#define RRTYPE_OPENPGPKEY_ATTRIBUTES 0
+
+static inline isc_result_t
+fromtext_openpgpkey(ARGS_FROMTEXT) {
+
+ REQUIRE(type == dns_rdatatype_openpgpkey);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+ UNUSED(options);
+ UNUSED(origin);
+
+ /*
+ * Keyring.
+ */
+ return (isc_base64_tobuffer(lexer, target, -1));
+}
+
+static inline isc_result_t
+totext_openpgpkey(ARGS_TOTEXT) {
+ isc_region_t sr;
+
+ REQUIRE(rdata->type == dns_rdatatype_openpgpkey);
+ REQUIRE(rdata->length != 0);
+
+ dns_rdata_toregion(rdata, &sr);
+
+ /*
+ * Keyring
+ */
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext("( ", target));
+
+ if ((tctx->flags & DNS_STYLEFLAG_NOCRYPTO) == 0) {
+ if (tctx->width == 0) /* No splitting */
+ RETERR(isc_base64_totext(&sr, 60, "", target));
+ else
+ RETERR(isc_base64_totext(&sr, tctx->width - 2,
+ tctx->linebreak, target));
+ } else
+ RETERR(str_totext("[omitted]", target));
+
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext(" )", target));
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+fromwire_openpgpkey(ARGS_FROMWIRE) {
+ isc_region_t sr;
+
+ REQUIRE(type == dns_rdatatype_openpgpkey);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(dctx);
+ UNUSED(options);
+
+ /*
+ * Keyring.
+ */
+ isc_buffer_activeregion(source, &sr);
+ if (sr.length < 1)
+ return (ISC_R_UNEXPECTEDEND);
+ isc_buffer_forward(source, sr.length);
+ return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline isc_result_t
+towire_openpgpkey(ARGS_TOWIRE) {
+ isc_region_t sr;
+
+ REQUIRE(rdata->type == dns_rdatatype_openpgpkey);
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(cctx);
+
+ dns_rdata_toregion(rdata, &sr);
+ return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline int
+compare_openpgpkey(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == dns_rdatatype_openpgpkey);
+ 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_openpgpkey(ARGS_FROMSTRUCT) {
+ dns_rdata_openpgpkey_t *sig = source;
+
+ REQUIRE(type == dns_rdatatype_openpgpkey);
+ REQUIRE(source != NULL);
+ REQUIRE(sig->common.rdtype == type);
+ REQUIRE(sig->common.rdclass == rdclass);
+ REQUIRE(sig->keyring != NULL && sig->length != 0);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ /*
+ * Keyring.
+ */
+ return (mem_tobuffer(target, sig->keyring, sig->length));
+}
+
+static inline isc_result_t
+tostruct_openpgpkey(ARGS_TOSTRUCT) {
+ isc_region_t sr;
+ dns_rdata_openpgpkey_t *sig = target;
+
+ REQUIRE(rdata->type == dns_rdatatype_openpgpkey);
+ 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);
+
+ /*
+ * Keyring.
+ */
+ sig->length = sr.length;
+ sig->keyring = mem_maybedup(mctx, sr.base, sig->length);
+ if (sig->keyring == NULL)
+ goto cleanup;
+
+ sig->mctx = mctx;
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ return (ISC_R_NOMEMORY);
+}
+
+static inline void
+freestruct_openpgpkey(ARGS_FREESTRUCT) {
+ dns_rdata_openpgpkey_t *sig = (dns_rdata_openpgpkey_t *) source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(sig->common.rdtype == dns_rdatatype_openpgpkey);
+
+ if (sig->mctx == NULL)
+ return;
+
+ if (sig->keyring != NULL)
+ isc_mem_free(sig->mctx, sig->keyring);
+ sig->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_openpgpkey(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == dns_rdatatype_openpgpkey);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_openpgpkey(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == dns_rdatatype_openpgpkey);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_openpgpkey(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_openpgpkey);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_openpgpkey(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_openpgpkey);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_openpgpkey(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == dns_rdatatype_openpgpkey);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+
+ return (isc_region_compare(&r1, &r2));
+}
+
+#endif /* RDATA_GENERIC_OPENPGPKEY_61_C */
diff --git a/lib/dns/rdata/generic/openpgpkey_61.h b/lib/dns/rdata/generic/openpgpkey_61.h
new file mode 100644
index 0000000..7c68e7f
--- /dev/null
+++ b/lib/dns/rdata/generic/openpgpkey_61.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef GENERIC_OPENPGPKEY_61_H
+#define GENERIC_OPENPGPKEY_61_H 1
+
+typedef struct dns_rdata_openpgpkey {
+ dns_rdatacommon_t common;
+ isc_mem_t * mctx;
+ uint16_t length;
+ unsigned char * keyring;
+} dns_rdata_openpgpkey_t;
+
+#endif /* GENERIC_OPENPGPKEY_61_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..b102cf2
--- /dev/null
+++ b/lib/dns/rdata/generic/opt_41.c
@@ -0,0 +1,413 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* 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 == dns_rdatatype_opt);
+
+ 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;
+ uint16_t option;
+ uint16_t length;
+ char buf[sizeof("64000 64000")];
+
+ /*
+ * OPT records do not have a text format.
+ */
+
+ REQUIRE(rdata->type == dns_rdatatype_opt);
+
+ 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);
+ snprintf(buf, sizeof(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;
+ if (tctx->width == 0) /* No splitting */
+ RETERR(isc_base64_totext(&or, 60, "", target));
+ else
+ 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;
+ uint16_t opt;
+ uint16_t length;
+ unsigned int total;
+
+ REQUIRE(type == dns_rdatatype_opt);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(dctx);
+ UNUSED(options);
+
+ isc_buffer_activeregion(source, &sregion);
+ if (sregion.length == 0)
+ return (ISC_R_SUCCESS);
+ total = 0;
+ while (sregion.length != 0) {
+ if (sregion.length < 4)
+ return (ISC_R_UNEXPECTEDEND);
+ opt = uint16_fromregion(&sregion);
+ isc_region_consume(&sregion, 2);
+ length = uint16_fromregion(&sregion);
+ isc_region_consume(&sregion, 2);
+ total += 4;
+ if (sregion.length < length)
+ return (ISC_R_UNEXPECTEDEND);
+ switch (opt) {
+ case DNS_OPT_CLIENT_SUBNET: {
+ uint16_t family;
+ uint8_t addrlen;
+ uint8_t scope;
+ uint8_t addrbytes;
+
+ if (length < 4)
+ return (DNS_R_OPTERR);
+ family = uint16_fromregion(&sregion);
+ isc_region_consume(&sregion, 2);
+ addrlen = uint8_fromregion(&sregion);
+ isc_region_consume(&sregion, 1);
+ scope = uint8_fromregion(&sregion);
+ isc_region_consume(&sregion, 1);
+
+ switch (family) {
+ case 0:
+ /*
+ * XXXMUKS: In queries and replies, if
+ * FAMILY is set to 0, SOURCE
+ * PREFIX-LENGTH and SCOPE PREFIX-LENGTH
+ * must be 0 and ADDRESS should not be
+ * present as the address and prefix
+ * lengths don't make sense because the
+ * family is unknown.
+ */
+ if (addrlen != 0U || scope != 0U)
+ return (DNS_R_OPTERR);
+ break;
+ case 1:
+ if (addrlen > 32U || scope > 32U)
+ return (DNS_R_OPTERR);
+ break;
+ case 2:
+ if (addrlen > 128U || scope > 128U)
+ return (DNS_R_OPTERR);
+ break;
+ default:
+ return (DNS_R_OPTERR);
+ }
+ addrbytes = (addrlen + 7) / 8;
+ if (addrbytes + 4 != length)
+ return (DNS_R_OPTERR);
+
+ if (addrbytes != 0U && (addrlen % 8) != 0) {
+ uint8_t bits = ~0U << (8 - (addrlen % 8));
+ bits &= sregion.base[addrbytes - 1];
+ if (bits != sregion.base[addrbytes - 1])
+ return (DNS_R_OPTERR);
+ }
+ isc_region_consume(&sregion, addrbytes);
+ break;
+ }
+ case DNS_OPT_EXPIRE:
+ /*
+ * Request has zero length. Response is 32 bits.
+ */
+ if (length != 0 && length != 4)
+ return (DNS_R_OPTERR);
+ isc_region_consume(&sregion, length);
+ break;
+ case DNS_OPT_COOKIE:
+ if (length != 8 && (length < 16 || length > 40))
+ return (DNS_R_OPTERR);
+ isc_region_consume(&sregion, length);
+ break;
+ case DNS_OPT_KEY_TAG:
+ if (length == 0 || (length % 2) != 0)
+ return (DNS_R_OPTERR);
+ isc_region_consume(&sregion, length);
+ break;
+ default:
+ isc_region_consume(&sregion, length);
+ break;
+ }
+ total += length;
+ }
+
+ isc_buffer_activeregion(source, &sregion);
+ isc_buffer_availableregion(target, &tregion);
+ if (tregion.length < total)
+ return (ISC_R_NOSPACE);
+ memmove(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 == dns_rdatatype_opt);
+
+ 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 == dns_rdatatype_opt);
+
+ 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;
+ uint16_t length;
+
+ REQUIRE(type == dns_rdatatype_opt);
+ 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 == dns_rdatatype_opt);
+ 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 == dns_rdatatype_opt);
+
+ 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 == dns_rdatatype_opt);
+
+ 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 == dns_rdatatype_opt);
+
+ UNUSED(rdata);
+ UNUSED(digest);
+ UNUSED(arg);
+
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static inline bool
+checkowner_opt(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_opt);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (dns_name_equal(name, dns_rootname));
+}
+
+static inline bool
+checknames_opt(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_opt);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_opt(ARGS_COMPARE) {
+ return (compare_opt(rdata1, rdata2));
+}
+
+isc_result_t
+dns_rdata_opt_first(dns_rdata_opt_t *opt) {
+
+ REQUIRE(opt != NULL);
+ REQUIRE(opt->common.rdtype == dns_rdatatype_opt);
+ REQUIRE(opt->options != NULL || opt->length == 0);
+
+ if (opt->length == 0)
+ return (ISC_R_NOMORE);
+
+ opt->offset = 0;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_rdata_opt_next(dns_rdata_opt_t *opt) {
+ isc_region_t r;
+ uint16_t length;
+
+ REQUIRE(opt != NULL);
+ REQUIRE(opt->common.rdtype == dns_rdatatype_opt);
+ REQUIRE(opt->options != NULL && opt->length != 0);
+ REQUIRE(opt->offset < opt->length);
+
+ INSIST(opt->offset + 4 <= opt->length);
+ r.base = opt->options + opt->offset + 2;
+ r.length = opt->length - opt->offset - 2;
+ length = uint16_fromregion(&r);
+ INSIST(opt->offset + 4 + length <= opt->length);
+ opt->offset = opt->offset + 4 + length;
+ if (opt->offset == opt->length)
+ return (ISC_R_NOMORE);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_rdata_opt_current(dns_rdata_opt_t *opt, dns_rdata_opt_opcode_t *opcode) {
+ isc_region_t r;
+
+ REQUIRE(opt != NULL);
+ REQUIRE(opcode != NULL);
+ REQUIRE(opt->common.rdtype == dns_rdatatype_opt);
+ REQUIRE(opt->options != NULL);
+ REQUIRE(opt->offset < opt->length);
+
+ INSIST(opt->offset + 4 <= opt->length);
+ r.base = opt->options + opt->offset;
+ r.length = opt->length - opt->offset;
+
+ opcode->opcode = uint16_fromregion(&r);
+ isc_region_consume(&r, 2);
+ opcode->length = uint16_fromregion(&r);
+ isc_region_consume(&r, 2);
+ opcode->data = r.base;
+ INSIST(opt->offset + 4 + opcode->length <= opt->length);
+
+ return (ISC_R_SUCCESS);
+}
+
+#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..5b31f4d
--- /dev/null
+++ b/lib/dns/rdata/generic/opt_41.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef GENERIC_OPT_41_H
+#define GENERIC_OPT_41_H 1
+
+
+/*!
+ * \brief Per RFC2671 */
+
+typedef struct dns_rdata_opt_opcode {
+ uint16_t opcode;
+ 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;
+ uint16_t length;
+ /* private */
+ 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..c31b29f
--- /dev/null
+++ b/lib/dns/rdata/generic/proforma.c
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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 == dns_rdatatype_proforma.c#);
+ REQUIRE(rdclass == #);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ false));
+
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static inline isc_result_t
+totext_#(ARGS_TOTEXT) {
+
+ REQUIRE(rdata->type == dns_rdatatype_proforma.c#);
+ REQUIRE(rdata->rdclass == #);
+ REQUIRE(rdata->length != 0); /* XXX */
+
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static inline isc_result_t
+fromwire_#(ARGS_FROMWIRE) {
+
+ REQUIRE(type == dns_rdatatype_proforma.c#);
+ 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 == dns_rdatatype_proforma.c#);
+ 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 == dns_rdatatype_proforma.crdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == dns_rdatatype_proforma.c#);
+ 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 == dns_rdatatype_proforma.c#);
+ REQUIRE(rdclass == #);
+ REQUIRE(source != NULL);
+ REQUIRE(#->common.rdtype == dns_rdatatype_proforma.ctype);
+ REQUIRE(#->common.rdclass == rdclass);
+
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static inline isc_result_t
+tostruct_#(ARGS_TOSTRUCT) {
+
+ REQUIRE(rdata->type == dns_rdatatype_proforma.c#);
+ 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 == dns_rdatatype_proforma.c#);
+ REQUIRE(#->common.rdclass == #);
+
+}
+
+static inline isc_result_t
+additionaldata_#(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == dns_rdatatype_proforma.c#);
+ 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 == dns_rdatatype_proforma.c#);
+ REQUIRE(rdata->rdclass == #);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_#(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_proforma.c#);
+ REQUIRE(rdclass == #);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_#(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_proforma.c#);
+ REQUIRE(rdata->rdclass == #);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_#(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1->type == dns_rdatatype_proforma.crdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == dns_rdatatype_proforma.c#);
+ 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));
+}
+
+#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..cb49ee4
--- /dev/null
+++ b/lib/dns/rdata/generic/proforma.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* */
+#ifndef GENERIC_PROFORMA_H
+#define GENERIC_PROFORMA_H 1
+
+
+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..a2f5ef1
--- /dev/null
+++ b/lib/dns/rdata/generic/ptr_12.c
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#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 == dns_rdatatype_ptr);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ false));
+
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ if (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) {
+ bool ok;
+ ok = dns_name_ishostname(&name, 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;
+ bool sub;
+
+ REQUIRE(rdata->type == dns_rdatatype_ptr);
+ 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 == dns_rdatatype_ptr);
+
+ 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 == dns_rdatatype_ptr);
+ 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 == dns_rdatatype_ptr);
+ 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 == dns_rdatatype_ptr);
+ 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 == dns_rdatatype_ptr);
+ 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 == dns_rdatatype_ptr);
+
+ 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 == dns_rdatatype_ptr);
+
+ 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 == dns_rdatatype_ptr);
+
+ dns_rdata_toregion(rdata, &r);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &r);
+
+ return (dns_name_digest(&name, digest, arg));
+}
+
+static inline bool
+checkowner_ptr(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_ptr);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (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_INITABSOLUTE(ip6_arpa_data, ip6_arpa_offsets);
+
+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_INITABSOLUTE(ip6_int_data, ip6_int_offsets);
+
+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_INITABSOLUTE(in_addr_arpa_data, in_addr_arpa_offsets);
+
+static inline bool
+checknames_ptr(ARGS_CHECKNAMES) {
+ isc_region_t region;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == dns_rdatatype_ptr);
+
+ if (rdata->rdclass != dns_rdataclass_in)
+ return (true);
+
+ if (dns_name_isdnssd(owner))
+ return (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, false)) {
+ if (bad != NULL)
+ dns_name_clone(&name, bad);
+ return (false);
+ }
+ }
+ return (true);
+}
+
+static inline int
+casecompare_ptr(ARGS_COMPARE) {
+ return (compare_ptr(rdata1, rdata2));
+}
+#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..d71ebd9
--- /dev/null
+++ b/lib/dns/rdata/generic/ptr_12.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* */
+#ifndef GENERIC_PTR_12_H
+#define GENERIC_PTR_12_H 1
+
+
+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/rkey_57.c b/lib/dns/rdata/generic/rkey_57.c
new file mode 100644
index 0000000..2475a48
--- /dev/null
+++ b/lib/dns/rdata/generic/rkey_57.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef RDATA_GENERIC_RKEY_57_C
+#define RDATA_GENERIC_RKEY_57_C
+
+#define RRTYPE_RKEY_ATTRIBUTES 0
+
+static inline isc_result_t
+fromtext_rkey(ARGS_FROMTEXT) {
+
+ REQUIRE(type == dns_rdatatype_rkey);
+
+ return (generic_fromtext_key(rdclass, type, lexer, origin,
+ options, target, callbacks));
+}
+
+static inline isc_result_t
+totext_rkey(ARGS_TOTEXT) {
+
+ REQUIRE(rdata != NULL);
+ REQUIRE(rdata->type == dns_rdatatype_rkey);
+
+ return (generic_totext_key(rdata, tctx, target));
+}
+
+static inline isc_result_t
+fromwire_rkey(ARGS_FROMWIRE) {
+
+ REQUIRE(type == dns_rdatatype_rkey);
+
+ return (generic_fromwire_key(rdclass, type, source, dctx,
+ options, target));
+}
+
+static inline isc_result_t
+towire_rkey(ARGS_TOWIRE) {
+ isc_region_t sr;
+
+ REQUIRE(rdata != NULL);
+ REQUIRE(rdata->type == dns_rdatatype_rkey);
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(cctx);
+
+ dns_rdata_toregion(rdata, &sr);
+ return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline int
+compare_rkey(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1 != NULL);
+ REQUIRE(rdata2 != NULL);
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == dns_rdatatype_rkey);
+ 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_rkey(ARGS_FROMSTRUCT) {
+
+ REQUIRE(type == dns_rdatatype_rkey);
+
+ return (generic_fromstruct_key(rdclass, type, source, target));
+}
+
+static inline isc_result_t
+tostruct_rkey(ARGS_TOSTRUCT) {
+ dns_rdata_rkey_t *rkey = target;
+
+ REQUIRE(rkey != NULL);
+ REQUIRE(rdata != NULL);
+ REQUIRE(rdata->type == dns_rdatatype_rkey);
+
+ rkey->common.rdclass = rdata->rdclass;
+ rkey->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&rkey->common, link);
+
+ return (generic_tostruct_key(rdata, target, mctx));
+}
+
+static inline void
+freestruct_rkey(ARGS_FREESTRUCT) {
+ dns_rdata_rkey_t *rkey = (dns_rdata_rkey_t *) source;
+
+ REQUIRE(rkey != NULL);
+ REQUIRE(rkey->common.rdtype == dns_rdatatype_rkey);
+
+ generic_freestruct_key(source);
+}
+
+static inline isc_result_t
+additionaldata_rkey(ARGS_ADDLDATA) {
+
+ REQUIRE(rdata->type == dns_rdatatype_rkey);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_rkey(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata != NULL);
+ REQUIRE(rdata->type == dns_rdatatype_rkey);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_rkey(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_rkey);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_rkey(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata != NULL);
+ REQUIRE(rdata->type == dns_rdatatype_rkey);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_rkey(ARGS_COMPARE) {
+
+ /*
+ * Treat ALG 253 (private DNS) subtype name case sensistively.
+ */
+ return (compare_rkey(rdata1, rdata2));
+}
+
+#endif /* RDATA_GENERIC_RKEY_57_C */
diff --git a/lib/dns/rdata/generic/rkey_57.h b/lib/dns/rdata/generic/rkey_57.h
new file mode 100644
index 0000000..000a725
--- /dev/null
+++ b/lib/dns/rdata/generic/rkey_57.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef GENERIC_RKEY_57_H
+#define GENERIC_RKEY_57_H 1
+
+typedef struct dns_rdata_key dns_rdata_rkey_t;
+
+#endif /* GENERIC_RKEY_57_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..2e91565
--- /dev/null
+++ b/lib/dns/rdata/generic/rp_17.c
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/* 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;
+ bool ok;
+
+ REQUIRE(type == dns_rdatatype_rp);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ if (origin == NULL)
+ origin = dns_rootname;
+
+ for (i = 0; i < 2; i++) {
+ RETERR(isc_lex_getmastertoken(lexer, &token,
+ isc_tokentype_string,
+ false));
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ RETTOK(dns_name_fromtext(&name, &buffer, origin,
+ options, target));
+ ok = 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;
+ bool sub;
+
+ REQUIRE(rdata->type == dns_rdatatype_rp);
+ 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 == dns_rdatatype_rp);
+
+ 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 == dns_rdatatype_rp);
+ 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 == dns_rdatatype_rp);
+ 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 == dns_rdatatype_rp);
+ 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 == dns_rdatatype_rp);
+ 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 == dns_rdatatype_rp);
+
+ 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 == dns_rdatatype_rp);
+
+ 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 == dns_rdatatype_rp);
+
+ 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 bool
+checkowner_rp(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_rp);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_rp(ARGS_CHECKNAMES) {
+ isc_region_t region;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == dns_rdatatype_rp);
+
+ 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 (false);
+ }
+ return (true);
+}
+
+static inline int
+casecompare_rp(ARGS_COMPARE) {
+ return (compare_rp(rdata1, rdata2));
+}
+#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..31b5768
--- /dev/null
+++ b/lib/dns/rdata/generic/rp_17.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef GENERIC_RP_17_H
+#define GENERIC_RP_17_H 1
+
+
+/*!
+ * \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..fb945ff
--- /dev/null
+++ b/lib/dns/rdata/generic/rrsig_46.c
@@ -0,0 +1,613 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* 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;
+ uint32_t time_signed, time_expire;
+
+ REQUIRE(type == dns_rdatatype_rrsig);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ /*
+ * Type covered.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ 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,
+ 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,
+ 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,
+ false));
+ RETERR(uint32_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * Signature expiration.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ false));
+ if (strlen(DNS_AS_STR(token)) <= 10U &&
+ *DNS_AS_STR(token) != '-' && *DNS_AS_STR(token) != '+') {
+ char *end;
+ unsigned long u;
+ uint64_t u64;
+
+ u64 = u = strtoul(DNS_AS_STR(token), &end, 10);
+ if (u == ULONG_MAX || *end != 0)
+ RETTOK(DNS_R_SYNTAX);
+ if (u64 > 0xffffffffUL)
+ RETTOK(ISC_R_RANGE);
+ time_expire = u;
+ } else
+ 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,
+ false));
+ if (strlen(DNS_AS_STR(token)) <= 10U &&
+ *DNS_AS_STR(token) != '-' && *DNS_AS_STR(token) != '+') {
+ char *end;
+ unsigned long u;
+ uint64_t u64;
+
+ u64 = u = strtoul(DNS_AS_STR(token), &end, 10);
+ if (u == ULONG_MAX || *end != 0)
+ RETTOK(DNS_R_SYNTAX);
+ if (u64 > 0xffffffffUL)
+ RETTOK(ISC_R_RANGE);
+ time_signed = u;
+ } else
+ 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,
+ false));
+ RETERR(uint16_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * Signer.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ false));
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ if (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")]; /* Also TYPE65000. */
+ dns_rdatatype_t covered;
+ unsigned long ttl;
+ unsigned long when;
+ unsigned long exp;
+ unsigned long foot;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == dns_rdatatype_rrsig);
+ 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 {
+ snprintf(buf, sizeof(buf), "TYPE%u", covered);
+ RETERR(str_totext(buf, target));
+ }
+ RETERR(str_totext(" ", target));
+
+ /*
+ * Algorithm.
+ */
+ snprintf(buf, sizeof(buf), "%u", sr.base[0]);
+ isc_region_consume(&sr, 1);
+ RETERR(str_totext(buf, target));
+ RETERR(str_totext(" ", target));
+
+ /*
+ * Labels.
+ */
+ snprintf(buf, sizeof(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);
+ snprintf(buf, sizeof(buf), "%lu", ttl);
+ RETERR(str_totext(buf, target));
+
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext(" (", target));
+ RETERR(str_totext(tctx->linebreak, target));
+
+ /*
+ * Sig exp.
+ */
+ exp = uint32_fromregion(&sr);
+ isc_region_consume(&sr, 4);
+ RETERR(dns_time32_totext(exp, target));
+ RETERR(str_totext(" ", 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);
+ snprintf(buf, sizeof(buf), "%lu", foot);
+ RETERR(str_totext(buf, target));
+ RETERR(str_totext(" ", target));
+
+ /*
+ * Signer.
+ */
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &sr);
+ isc_region_consume(&sr, name_length(&name));
+ RETERR(dns_name_totext(&name, false, target));
+
+ /*
+ * Sig.
+ */
+ RETERR(str_totext(tctx->linebreak, target));
+ if ((tctx->flags & DNS_STYLEFLAG_NOCRYPTO) == 0) {
+ if (tctx->width == 0) /* No splitting */
+ RETERR(isc_base64_totext(&sr, 60, "", target));
+ else
+ RETERR(isc_base64_totext(&sr, tctx->width - 2,
+ tctx->linebreak, target));
+ } else
+ RETERR(str_totext("[omitted]", 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 == dns_rdatatype_rrsig);
+
+ 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 == dns_rdatatype_rrsig);
+ 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 == dns_rdatatype_rrsig);
+ 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 == dns_rdatatype_rrsig);
+ 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 == dns_rdatatype_rrsig);
+ 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 == dns_rdatatype_rrsig);
+
+ 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 == dns_rdatatype_rrsig);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_rrsig(ARGS_DIGEST) {
+
+ REQUIRE(rdata->type == dns_rdatatype_rrsig);
+
+ 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 == dns_rdatatype_rrsig);
+
+ dns_rdata_toregion(rdata, &r);
+ type = uint16_fromregion(&r);
+
+ return (type);
+}
+
+static inline bool
+checkowner_rrsig(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_rrsig);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_rrsig(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_rrsig);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_rrsig(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 == dns_rdatatype_rrsig);
+ 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));
+}
+
+#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..6dfbd42
--- /dev/null
+++ b/lib/dns/rdata/generic/rrsig_46.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef GENERIC_DNSSIG_46_H
+#define GENERIC_DNSSIG_46_H 1
+
+
+/*!
+ * \brief Per RFC2535 */
+typedef struct dns_rdata_rrsig {
+ dns_rdatacommon_t common;
+ isc_mem_t * mctx;
+ dns_rdatatype_t covered;
+ dns_secalg_t algorithm;
+ uint8_t labels;
+ uint32_t originalttl;
+ uint32_t timeexpire;
+ uint32_t timesigned;
+ uint16_t keyid;
+ dns_name_t signer;
+ 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..e6450cb
--- /dev/null
+++ b/lib/dns/rdata/generic/rt_21.c
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* 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;
+ bool ok;
+
+ REQUIRE(type == dns_rdatatype_rt);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ 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,
+ false));
+
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ if (origin == NULL)
+ origin = dns_rootname;
+ RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target));
+ ok = true;
+ if ((options & DNS_RDATA_CHECKNAMES) != 0)
+ ok = dns_name_ishostname(&name, 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;
+ bool sub;
+ char buf[sizeof("64000")];
+ unsigned short num;
+
+ REQUIRE(rdata->type == dns_rdatatype_rt);
+ 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);
+ snprintf(buf, sizeof(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 == dns_rdatatype_rt);
+
+ 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);
+ memmove(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 == dns_rdatatype_rt);
+ 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);
+ memmove(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 == dns_rdatatype_rt);
+ 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 == dns_rdatatype_rt);
+ 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 == dns_rdatatype_rt);
+ 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 == dns_rdatatype_rt);
+
+ 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 == dns_rdatatype_rt);
+
+ 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 == dns_rdatatype_rt);
+
+ 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 bool
+checkowner_rt(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_rt);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_rt(ARGS_CHECKNAMES) {
+ isc_region_t region;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == dns_rdatatype_rt);
+
+ 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, false)) {
+ if (bad != NULL)
+ dns_name_clone(&name, bad);
+ return (false);
+ }
+ return (true);
+}
+
+static inline int
+casecompare_rt(ARGS_COMPARE) {
+ return (compare_rt(rdata1, rdata2));
+}
+
+#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..a9082c7
--- /dev/null
+++ b/lib/dns/rdata/generic/rt_21.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef GENERIC_RT_21_H
+#define GENERIC_RT_21_H 1
+
+
+/*!
+ * \brief Per RFC1183 */
+
+typedef struct dns_rdata_rt {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ 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..ca0ce4e
--- /dev/null
+++ b/lib/dns/rdata/generic/sig_24.c
@@ -0,0 +1,575 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* 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;
+ uint32_t time_signed, time_expire;
+
+ REQUIRE(type == dns_rdatatype_sig);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ /*
+ * Type covered.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ 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,
+ 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,
+ 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,
+ false));
+ RETERR(uint32_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * Signature expiration.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ 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,
+ 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,
+ false));
+ RETERR(uint16_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * Signer.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ false));
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ if (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;
+ bool sub;
+
+ REQUIRE(rdata->type == dns_rdatatype_sig);
+ 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 {
+ snprintf(buf, sizeof(buf), "%u", covered);
+ RETERR(str_totext(buf, target));
+ }
+ RETERR(str_totext(" ", target));
+
+ /*
+ * Algorithm.
+ */
+ snprintf(buf, sizeof(buf), "%u", sr.base[0]);
+ isc_region_consume(&sr, 1);
+ RETERR(str_totext(buf, target));
+ RETERR(str_totext(" ", target));
+
+ /*
+ * Labels.
+ */
+ snprintf(buf, sizeof(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);
+ snprintf(buf, sizeof(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);
+ snprintf(buf, sizeof(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));
+ if (tctx->width == 0) /* No splitting */
+ RETERR(isc_base64_totext(&sr, 60, "", target));
+ else
+ 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 == dns_rdatatype_sig);
+
+ 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 == dns_rdatatype_sig);
+ 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 == dns_rdatatype_sig);
+ 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 == dns_rdatatype_sig);
+ 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 == dns_rdatatype_sig);
+ 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 == dns_rdatatype_sig);
+
+ 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 == dns_rdatatype_sig);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_sig(ARGS_DIGEST) {
+
+ REQUIRE(rdata->type == dns_rdatatype_sig);
+
+ 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 == dns_rdatatype_sig);
+
+ dns_rdata_toregion(rdata, &r);
+ type = uint16_fromregion(&r);
+
+ return (type);
+}
+
+static inline bool
+checkowner_sig(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_sig);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_sig(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_sig);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_sig(ARGS_COMPARE) {
+ return (compare_sig(rdata1, rdata2));
+}
+#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..a3882d9
--- /dev/null
+++ b/lib/dns/rdata/generic/sig_24.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef GENERIC_SIG_24_H
+#define GENERIC_SIG_24_H 1
+
+
+/*!
+ * \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;
+ uint8_t labels;
+ uint32_t originalttl;
+ uint32_t timeexpire;
+ uint32_t timesigned;
+ uint16_t keyid;
+ dns_name_t signer;
+ uint16_t siglen;
+ unsigned char * signature;
+} dns_rdata_sig_t;
+
+
+#endif /* GENERIC_SIG_24_H */
diff --git a/lib/dns/rdata/generic/sink_40.c b/lib/dns/rdata/generic/sink_40.c
new file mode 100644
index 0000000..1289414
--- /dev/null
+++ b/lib/dns/rdata/generic/sink_40.c
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef RDATA_GENERIC_SINK_40_C
+#define RDATA_GENERIC_SINK_40_C
+
+#include <dst/dst.h>
+
+#define RRTYPE_SINK_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_sink(ARGS_FROMTEXT) {
+ isc_token_t token;
+
+ REQUIRE(type == dns_rdatatype_sink);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(callbacks);
+
+ /* meaning */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ false));
+ if (token.value.as_ulong > 0xffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint8_tobuffer(token.value.as_ulong, target));
+
+ /* coding */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ false));
+ if (token.value.as_ulong > 0xffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint8_tobuffer(token.value.as_ulong, target));
+
+ /* subcoding */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ false));
+ if (token.value.as_ulong > 0xffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint8_tobuffer(token.value.as_ulong, target));
+
+ return(isc_base64_tobuffer(lexer, target, -1));
+}
+
+static inline isc_result_t
+totext_sink(ARGS_TOTEXT) {
+ isc_region_t sr;
+ char buf[sizeof("255 255 255")];
+ uint8_t meaning, coding, subcoding;
+
+ REQUIRE(rdata->type == dns_rdatatype_sink);
+ REQUIRE(rdata->length >= 3);
+
+ dns_rdata_toregion(rdata, &sr);
+
+ /* Meaning, Coding and Subcoding */
+ meaning = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+ coding = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+ subcoding = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+ snprintf(buf, sizeof(buf), "%u %u %u", meaning, coding, subcoding);
+ RETERR(str_totext(buf, target));
+
+ if (sr.length == 0U)
+ return (ISC_R_SUCCESS);
+
+ /* data */
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext(" (", target));
+
+ RETERR(str_totext(tctx->linebreak, target));
+
+ if (tctx->width == 0) /* No splitting */
+ RETERR(isc_base64_totext(&sr, 60, "", target));
+ else
+ 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_sink(ARGS_FROMWIRE) {
+ isc_region_t sr;
+
+ REQUIRE(type == dns_rdatatype_sink);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(dctx);
+ UNUSED(options);
+
+ isc_buffer_activeregion(source, &sr);
+ if (sr.length < 3)
+ return (ISC_R_UNEXPECTEDEND);
+
+ RETERR(mem_tobuffer(target, sr.base, sr.length));
+ isc_buffer_forward(source, sr.length);
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+towire_sink(ARGS_TOWIRE) {
+
+ REQUIRE(rdata->type == dns_rdatatype_sink);
+ REQUIRE(rdata->length >= 3);
+
+ UNUSED(cctx);
+
+ return (mem_tobuffer(target, rdata->data, rdata->length));
+}
+
+static inline int
+compare_sink(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == dns_rdatatype_sink);
+ REQUIRE(rdata1->length >= 3);
+ REQUIRE(rdata2->length >= 3);
+
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+ return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+fromstruct_sink(ARGS_FROMSTRUCT) {
+ dns_rdata_sink_t *sink = source;
+
+ REQUIRE(type == dns_rdatatype_sink);
+ REQUIRE(source != NULL);
+ REQUIRE(sink->common.rdtype == type);
+ REQUIRE(sink->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ /* Meaning */
+ RETERR(uint8_tobuffer(sink->meaning, target));
+
+ /* Coding */
+ RETERR(uint8_tobuffer(sink->coding, target));
+
+ /* Subcoding */
+ RETERR(uint8_tobuffer(sink->subcoding, target));
+
+ /* Data */
+ return (mem_tobuffer(target, sink->data, sink->datalen));
+}
+
+static inline isc_result_t
+tostruct_sink(ARGS_TOSTRUCT) {
+ dns_rdata_sink_t *sink = target;
+ isc_region_t sr;
+
+ REQUIRE(rdata->type == dns_rdatatype_sink);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length >= 3);
+
+ sink->common.rdclass = rdata->rdclass;
+ sink->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&sink->common, link);
+
+ dns_rdata_toregion(rdata, &sr);
+
+ /* Meaning */
+ if (sr.length < 1)
+ return (ISC_R_UNEXPECTEDEND);
+ sink->meaning = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+
+ /* Coding */
+ if (sr.length < 1)
+ return (ISC_R_UNEXPECTEDEND);
+ sink->coding = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+
+ /* Subcoding */
+ if (sr.length < 1)
+ return (ISC_R_UNEXPECTEDEND);
+ sink->subcoding = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+
+ /* Data */
+ sink->datalen = sr.length;
+ sink->data = mem_maybedup(mctx, sr.base, sink->datalen);
+ if (sink->data == NULL)
+ return (ISC_R_NOMEMORY);
+
+ sink->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_sink(ARGS_FREESTRUCT) {
+ dns_rdata_sink_t *sink = (dns_rdata_sink_t *) source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(sink->common.rdtype == dns_rdatatype_sink);
+
+ if (sink->mctx == NULL)
+ return;
+
+ if (sink->data != NULL)
+ isc_mem_free(sink->mctx, sink->data);
+ sink->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_sink(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == dns_rdatatype_sink);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_sink(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == dns_rdatatype_sink);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_sink(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_sink);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_sink(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_sink);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_sink(ARGS_COMPARE) {
+ return (compare_sink(rdata1, rdata2));
+}
+#endif /* RDATA_GENERIC_SINK_40_C */
diff --git a/lib/dns/rdata/generic/sink_40.h b/lib/dns/rdata/generic/sink_40.h
new file mode 100644
index 0000000..4e6e01e
--- /dev/null
+++ b/lib/dns/rdata/generic/sink_40.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef GENERIC_SINK_40_H
+#define GENERIC_SINK_40_H 1
+
+typedef struct dns_rdata_sink_t {
+ dns_rdatacommon_t common;
+ isc_mem_t * mctx;
+ uint8_t meaning;
+ uint8_t coding;
+ uint8_t subcoding;
+ uint16_t datalen;
+ unsigned char * data;
+} dns_rdata_sink_t;
+
+#endif /* GENERIC_SINK_40_H */
diff --git a/lib/dns/rdata/generic/smimea_53.c b/lib/dns/rdata/generic/smimea_53.c
new file mode 100644
index 0000000..efd7f01
--- /dev/null
+++ b/lib/dns/rdata/generic/smimea_53.c
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef RDATA_GENERIC_SMIMEA_53_C
+#define RDATA_GENERIC_SMIMEA_53_C
+
+#define RRTYPE_SMIMEA_ATTRIBUTES 0
+
+static inline isc_result_t
+fromtext_smimea(ARGS_FROMTEXT) {
+
+ REQUIRE(type == dns_rdatatype_smimea);
+
+ return (generic_fromtext_tlsa(rdclass, type, lexer, origin, options,
+ target, callbacks));
+}
+
+static inline isc_result_t
+totext_smimea(ARGS_TOTEXT) {
+
+ REQUIRE(rdata->type == dns_rdatatype_smimea);
+
+ return (generic_totext_tlsa(rdata, tctx, target));
+}
+
+static inline isc_result_t
+fromwire_smimea(ARGS_FROMWIRE) {
+
+ REQUIRE(type == dns_rdatatype_smimea);
+
+ return (generic_fromwire_tlsa(rdclass, type, source, dctx, options,
+ target));
+}
+
+static inline isc_result_t
+towire_smimea(ARGS_TOWIRE) {
+ isc_region_t sr;
+
+ REQUIRE(rdata->type == dns_rdatatype_smimea);
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(cctx);
+
+ dns_rdata_toregion(rdata, &sr);
+ return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline int
+compare_smimea(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == dns_rdatatype_smimea);
+ 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_smimea(ARGS_FROMSTRUCT) {
+
+ REQUIRE(type == dns_rdatatype_smimea);
+
+ return (generic_fromstruct_tlsa(rdclass, type, source, target));
+}
+
+static inline isc_result_t
+tostruct_smimea(ARGS_TOSTRUCT) {
+ dns_rdata_smimea_t *smimea = target;
+
+ REQUIRE(rdata->type == dns_rdatatype_smimea);
+ REQUIRE(target != NULL);
+
+ smimea->common.rdclass = rdata->rdclass;
+ smimea->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&smimea->common, link);
+
+ return (generic_tostruct_tlsa(rdata, target, mctx));
+}
+
+static inline void
+freestruct_smimea(ARGS_FREESTRUCT) {
+ dns_rdata_smimea_t *smimea = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(smimea->common.rdtype == dns_rdatatype_smimea);
+
+ generic_freestruct_tlsa(source);
+}
+
+static inline isc_result_t
+additionaldata_smimea(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == dns_rdatatype_smimea);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_smimea(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == dns_rdatatype_smimea);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_smimea(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_smimea);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_smimea(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_smimea);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_smimea(ARGS_COMPARE) {
+ return (compare_smimea(rdata1, rdata2));
+}
+
+#endif /* RDATA_GENERIC_SMIMEA_53_C */
diff --git a/lib/dns/rdata/generic/smimea_53.h b/lib/dns/rdata/generic/smimea_53.h
new file mode 100644
index 0000000..cabec2b
--- /dev/null
+++ b/lib/dns/rdata/generic/smimea_53.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef GENERIC_SMIMEA_53_H
+#define GENERIC_SMIMEA_53_H 1
+
+typedef struct dns_rdata_tlsa dns_rdata_smimea_t;
+
+#endif /* GENERIC_SMIMEA_53_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..c21d64b
--- /dev/null
+++ b/lib/dns/rdata/generic/soa_6.c
@@ -0,0 +1,438 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#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;
+ uint32_t n;
+ bool ok;
+
+ REQUIRE(type == dns_rdatatype_soa);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ if (origin == NULL)
+ origin = dns_rootname;
+
+ for (i = 0; i < 2; i++) {
+ RETERR(isc_lex_getmastertoken(lexer, &token,
+ isc_tokentype_string,
+ false));
+
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ RETTOK(dns_name_fromtext(&name, &buffer, origin,
+ options, target));
+ ok = true;
+ if ((options & DNS_RDATA_CHECKNAMES) != 0)
+ switch (i) {
+ case 0:
+ ok = dns_name_ishostname(&name, 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,
+ false));
+ RETERR(uint32_tobuffer(token.value.as_ulong, target));
+
+ for (i = 0; i < 4; i++) {
+ RETERR(isc_lex_getmastertoken(lexer, &token,
+ isc_tokentype_string,
+ 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;
+ bool sub;
+ int i;
+ bool multiline;
+ bool comm;
+
+ REQUIRE(rdata->type == dns_rdatatype_soa);
+ REQUIRE(rdata->length != 0);
+
+ multiline = (tctx->flags & DNS_STYLEFLAG_MULTILINE);
+ comm = (multiline) ?
+ (tctx->flags & DNS_STYLEFLAG_RRCOMMENT) :
+ false;
+
+ 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("0123456789 ; ")];
+ unsigned long num;
+ num = uint32_fromregion(&dregion);
+ isc_region_consume(&dregion, 4);
+ snprintf(buf, sizeof(buf), comm ? "%-10lu ; " : "%lu", num);
+ RETERR(str_totext(buf, target));
+ if (comm) {
+ 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, 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 == dns_rdatatype_soa);
+
+ 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);
+
+ memmove(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 == dns_rdatatype_soa);
+ 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);
+
+ memmove(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 == dns_rdatatype_soa);
+ 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 == dns_rdatatype_soa);
+ 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 == dns_rdatatype_soa);
+ 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 == dns_rdatatype_soa);
+
+ 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 == dns_rdatatype_soa);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_soa(ARGS_DIGEST) {
+ isc_region_t r;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == dns_rdatatype_soa);
+
+ 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 bool
+checkowner_soa(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_soa);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_soa(ARGS_CHECKNAMES) {
+ isc_region_t region;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == dns_rdatatype_soa);
+
+ UNUSED(owner);
+
+ dns_rdata_toregion(rdata, &region);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &region);
+ if (!dns_name_ishostname(&name, false)) {
+ if (bad != NULL)
+ dns_name_clone(&name, bad);
+ return (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 (false);
+ }
+ return (true);
+}
+
+static inline int
+casecompare_soa(ARGS_COMPARE) {
+ return (compare_soa(rdata1, rdata2));
+}
+
+#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..c9be985
--- /dev/null
+++ b/lib/dns/rdata/generic/soa_6.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* */
+#ifndef GENERIC_SOA_6_H
+#define GENERIC_SOA_6_H 1
+
+
+typedef struct dns_rdata_soa {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ dns_name_t origin;
+ dns_name_t contact;
+ uint32_t serial; /*%< host order */
+ uint32_t refresh; /*%< host order */
+ uint32_t retry; /*%< host order */
+ uint32_t expire; /*%< host order */
+ 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..c41edf6
--- /dev/null
+++ b/lib/dns/rdata/generic/spf_99.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#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) {
+
+ REQUIRE(type == dns_rdatatype_spf);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(callbacks);
+
+ return (generic_fromtext_txt(rdclass, type, lexer, origin, options,
+ target, callbacks));
+}
+
+static inline isc_result_t
+totext_spf(ARGS_TOTEXT) {
+
+ UNUSED(tctx);
+
+ REQUIRE(rdata->type == dns_rdatatype_spf);
+
+ return (generic_totext_txt(rdata, tctx, target));
+}
+
+static inline isc_result_t
+fromwire_spf(ARGS_FROMWIRE) {
+
+ REQUIRE(type == dns_rdatatype_spf);
+
+ UNUSED(type);
+ UNUSED(dctx);
+ UNUSED(rdclass);
+ UNUSED(options);
+
+ return (generic_fromwire_txt(rdclass, type, source, dctx, options,
+ target));
+}
+
+static inline isc_result_t
+towire_spf(ARGS_TOWIRE) {
+
+ REQUIRE(rdata->type == dns_rdatatype_spf);
+
+ UNUSED(cctx);
+
+ return (mem_tobuffer(target, rdata->data, rdata->length));
+}
+
+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 == dns_rdatatype_spf);
+
+ 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) {
+
+ REQUIRE(type == dns_rdatatype_spf);
+
+ return (generic_fromstruct_txt(rdclass, type, source, target));
+}
+
+static inline isc_result_t
+tostruct_spf(ARGS_TOSTRUCT) {
+ dns_rdata_spf_t *spf = target;
+
+ REQUIRE(rdata->type == dns_rdatatype_spf);
+ REQUIRE(target != NULL);
+
+ spf->common.rdclass = rdata->rdclass;
+ spf->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&spf->common, link);
+
+ return (generic_tostruct_txt(rdata, target, mctx));
+}
+
+static inline void
+freestruct_spf(ARGS_FREESTRUCT) {
+ dns_rdata_spf_t *txt = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(txt->common.rdtype == dns_rdatatype_spf);
+
+ generic_freestruct_txt(source);
+}
+
+static inline isc_result_t
+additionaldata_spf(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == dns_rdatatype_spf);
+
+ 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 == dns_rdatatype_spf);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_spf(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_spf);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_spf(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_spf);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_spf(ARGS_COMPARE) {
+ return (compare_spf(rdata1, rdata2));
+}
+#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..f10b790
--- /dev/null
+++ b/lib/dns/rdata/generic/spf_99.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef GENERIC_SPF_99_H
+#define GENERIC_SPF_99_H 1
+
+
+typedef struct dns_rdata_spf_string {
+ 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;
+ uint16_t txt_len;
+ /* private */
+ uint16_t offset;
+} dns_rdata_spf_t;
+
+/*
+ * ISC_LANG_BEGINDECLS and ISC_LANG_ENDDECLS are already done
+ * via rdatastructpre.h and rdatastructsuf.h.
+ */
+#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..1b5d7ca
--- /dev/null
+++ b/lib/dns/rdata/generic/sshfp_44.c
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/* 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 == dns_rdatatype_sshfp);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(callbacks);
+
+ /*
+ * Algorithm.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ 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,
+ false));
+ if (token.value.as_ulong > 0xffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint8_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * 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 == dns_rdatatype_sshfp);
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(tctx);
+
+ dns_rdata_toregion(rdata, &sr);
+
+ /*
+ * Algorithm.
+ */
+ n = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+ snprintf(buf, sizeof(buf), "%u ", n);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * Digest type.
+ */
+ n = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+ snprintf(buf, sizeof(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));
+ if (tctx->width == 0) /* No splitting */
+ RETERR(isc_hex_totext(&sr, 0, "", target));
+ else
+ 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 == dns_rdatatype_sshfp);
+
+ 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 == dns_rdatatype_sshfp);
+ 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 == dns_rdatatype_sshfp);
+ 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 == dns_rdatatype_sshfp);
+ 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 == dns_rdatatype_sshfp);
+ 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 == dns_rdatatype_sshfp);
+
+ 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 == dns_rdatatype_sshfp);
+
+ 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 == dns_rdatatype_sshfp);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_sshfp(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_sshfp);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_sshfp(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_sshfp);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_sshfp(ARGS_COMPARE) {
+ return (compare_sshfp(rdata1, rdata2));
+}
+
+#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..ba1129a
--- /dev/null
+++ b/lib/dns/rdata/generic/sshfp_44.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*!
+ * \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;
+ uint8_t algorithm;
+ uint8_t digest_type;
+ uint16_t length;
+ unsigned char *digest;
+} dns_rdata_sshfp_t;
+
+#endif /* GENERIC_SSHFP_44_H */
diff --git a/lib/dns/rdata/generic/ta_32768.c b/lib/dns/rdata/generic/ta_32768.c
new file mode 100644
index 0000000..12ec6e7
--- /dev/null
+++ b/lib/dns/rdata/generic/ta_32768.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* http://www.watson.org/~weiler/INI1999-19.pdf */
+
+#ifndef RDATA_GENERIC_TA_32768_C
+#define RDATA_GENERIC_TA_32768_C
+
+#define RRTYPE_TA_ATTRIBUTES 0
+
+static inline isc_result_t
+fromtext_ta(ARGS_FROMTEXT) {
+
+ REQUIRE(type == dns_rdatatype_ta);
+
+ return (generic_fromtext_ds(rdclass, type, lexer, origin, options,
+ target, callbacks));
+}
+
+static inline isc_result_t
+totext_ta(ARGS_TOTEXT) {
+
+ REQUIRE(rdata->type == dns_rdatatype_ta);
+
+ return (generic_totext_ds(rdata, tctx, target));
+}
+
+static inline isc_result_t
+fromwire_ta(ARGS_FROMWIRE) {
+
+ REQUIRE(type == dns_rdatatype_ta);
+
+ return (generic_fromwire_ds(rdclass, type, source, dctx, options,
+ target));
+}
+
+static inline isc_result_t
+towire_ta(ARGS_TOWIRE) {
+ isc_region_t sr;
+
+ REQUIRE(rdata->type == dns_rdatatype_ta);
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(cctx);
+
+ dns_rdata_toregion(rdata, &sr);
+ return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline int
+compare_ta(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == dns_rdatatype_ta);
+ 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_ta(ARGS_FROMSTRUCT) {
+
+ REQUIRE(type == dns_rdatatype_ta);
+
+ return (generic_fromstruct_ds(rdclass, type, source, target));
+}
+
+static inline isc_result_t
+tostruct_ta(ARGS_TOSTRUCT) {
+ dns_rdata_ds_t *ds = target;
+
+ REQUIRE(rdata->type == dns_rdatatype_ta);
+
+ /*
+ * Checked by generic_tostruct_ds().
+ */
+ ds->common.rdclass = rdata->rdclass;
+ ds->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&ds->common, link);
+
+ return (generic_tostruct_ds(rdata, target, mctx));
+}
+
+static inline void
+freestruct_ta(ARGS_FREESTRUCT) {
+ dns_rdata_ta_t *ds = source;
+
+ REQUIRE(ds != NULL);
+ REQUIRE(ds->common.rdtype == dns_rdatatype_ta);
+
+ 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_ta(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == dns_rdatatype_ta);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_ta(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == dns_rdatatype_ta);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_ta(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_ta);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_ta(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_ta);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_ta(ARGS_COMPARE) {
+ return (compare_ta(rdata1, rdata2));
+}
+
+#endif /* RDATA_GENERIC_TA_32768_C */
diff --git a/lib/dns/rdata/generic/ta_32768.h b/lib/dns/rdata/generic/ta_32768.h
new file mode 100644
index 0000000..1bd7a28
--- /dev/null
+++ b/lib/dns/rdata/generic/ta_32768.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef GENERIC_TA_32768_H
+#define GENERIC_TA_32768_H 1
+
+/*
+ * TA records are identical to DS records.
+ */
+typedef struct dns_rdata_ds dns_rdata_ta_t;
+
+#endif /* GENERIC_TA_32768_H */
diff --git a/lib/dns/rdata/generic/talink_58.c b/lib/dns/rdata/generic/talink_58.c
new file mode 100644
index 0000000..7f6bacb
--- /dev/null
+++ b/lib/dns/rdata/generic/talink_58.c
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef RDATA_GENERIC_TALINK_58_C
+#define RDATA_GENERIC_TALINK_58_C
+
+#define RRTYPE_TALINK_ATTRIBUTES 0
+
+static inline isc_result_t
+fromtext_talink(ARGS_FROMTEXT) {
+ isc_token_t token;
+ dns_name_t name;
+ isc_buffer_t buffer;
+ int i;
+
+ REQUIRE(type == dns_rdatatype_talink);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ if (origin == NULL)
+ origin = dns_rootname;
+
+ for (i = 0; i < 2; i++) {
+ RETERR(isc_lex_getmastertoken(lexer, &token,
+ isc_tokentype_string,
+ false));
+
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ RETTOK(dns_name_fromtext(&name, &buffer, origin,
+ options, target));
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_talink(ARGS_TOTEXT) {
+ isc_region_t dregion;
+ dns_name_t prev;
+ dns_name_t next;
+ dns_name_t prefix;
+ bool sub;
+
+ REQUIRE(rdata->type == dns_rdatatype_talink);
+ REQUIRE(rdata->length != 0);
+
+ dns_name_init(&prev, NULL);
+ dns_name_init(&next, NULL);
+ dns_name_init(&prefix, NULL);
+
+ dns_rdata_toregion(rdata, &dregion);
+
+ dns_name_fromregion(&prev, &dregion);
+ isc_region_consume(&dregion, name_length(&prev));
+
+ dns_name_fromregion(&next, &dregion);
+ isc_region_consume(&dregion, name_length(&next));
+
+ sub = name_prefix(&prev, tctx->origin, &prefix);
+ RETERR(dns_name_totext(&prefix, sub, target));
+
+ RETERR(str_totext(" ", target));
+
+ sub = name_prefix(&next, tctx->origin, &prefix);
+ return(dns_name_totext(&prefix, sub, target));
+}
+
+static inline isc_result_t
+fromwire_talink(ARGS_FROMWIRE) {
+ dns_name_t prev;
+ dns_name_t next;
+
+ REQUIRE(type == dns_rdatatype_talink);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_decompress_setmethods(dctx, DNS_COMPRESS_NONE);
+
+ dns_name_init(&prev, NULL);
+ dns_name_init(&next, NULL);
+
+ RETERR(dns_name_fromwire(&prev, source, dctx, options, target));
+ return(dns_name_fromwire(&next, source, dctx, options, target));
+}
+
+static inline isc_result_t
+towire_talink(ARGS_TOWIRE) {
+ isc_region_t sregion;
+ dns_name_t prev;
+ dns_name_t next;
+ dns_offsets_t moffsets;
+ dns_offsets_t roffsets;
+
+ REQUIRE(rdata->type == dns_rdatatype_talink);
+ REQUIRE(rdata->length != 0);
+
+ dns_compress_setmethods(cctx, DNS_COMPRESS_NONE);
+
+ dns_name_init(&prev, moffsets);
+ dns_name_init(&next, roffsets);
+
+ dns_rdata_toregion(rdata, &sregion);
+
+ dns_name_fromregion(&prev, &sregion);
+ isc_region_consume(&sregion, name_length(&prev));
+ RETERR(dns_name_towire(&prev, cctx, target));
+
+ dns_name_fromregion(&next, &sregion);
+ isc_region_consume(&sregion, name_length(&next));
+ return(dns_name_towire(&next, cctx, target));
+}
+
+static inline int
+compare_talink(ARGS_COMPARE) {
+ isc_region_t region1;
+ isc_region_t region2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == dns_rdatatype_talink);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_rdata_toregion(rdata1, &region1);
+ dns_rdata_toregion(rdata2, &region2);
+ return (isc_region_compare(&region1, &region2));
+}
+
+static inline isc_result_t
+fromstruct_talink(ARGS_FROMSTRUCT) {
+ dns_rdata_talink_t *talink = source;
+ isc_region_t region;
+
+ REQUIRE(type == dns_rdatatype_talink);
+ REQUIRE(source != NULL);
+ REQUIRE(talink->common.rdtype == type);
+ REQUIRE(talink->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_name_toregion(&talink->prev, &region);
+ RETERR(isc_buffer_copyregion(target, &region));
+ dns_name_toregion(&talink->next, &region);
+ return(isc_buffer_copyregion(target, &region));
+}
+
+static inline isc_result_t
+tostruct_talink(ARGS_TOSTRUCT) {
+ isc_region_t region;
+ dns_rdata_talink_t *talink = target;
+ dns_name_t name;
+ isc_result_t result;
+
+ REQUIRE(rdata->type == dns_rdatatype_talink);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ talink->common.rdclass = rdata->rdclass;
+ talink->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&talink->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(&talink->prev, NULL);
+ RETERR(name_duporclone(&name, mctx, &talink->prev));
+
+ dns_name_fromregion(&name, &region);
+ isc_region_consume(&region, name_length(&name));
+ dns_name_init(&talink->next, NULL);
+ result = name_duporclone(&name, mctx, &talink->next);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ talink->mctx = mctx;
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (mctx != NULL)
+ dns_name_free(&talink->prev, mctx);
+ return (ISC_R_NOMEMORY);
+}
+
+static inline void
+freestruct_talink(ARGS_FREESTRUCT) {
+ dns_rdata_talink_t *talink = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(talink->common.rdtype == dns_rdatatype_talink);
+
+ if (talink->mctx == NULL)
+ return;
+
+ dns_name_free(&talink->prev, talink->mctx);
+ dns_name_free(&talink->next, talink->mctx);
+ talink->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_talink(ARGS_ADDLDATA) {
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ REQUIRE(rdata->type == dns_rdatatype_talink);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_talink(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == dns_rdatatype_talink);
+
+ dns_rdata_toregion(rdata, &r);
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_talink(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_talink);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_talink(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_talink);
+
+ UNUSED(bad);
+ UNUSED(owner);
+
+ return (true);
+}
+
+static inline int
+casecompare_talink(ARGS_COMPARE) {
+ return (compare_talink(rdata1, rdata2));
+}
+
+#endif /* RDATA_GENERIC_TALINK_58_C */
diff --git a/lib/dns/rdata/generic/talink_58.h b/lib/dns/rdata/generic/talink_58.h
new file mode 100644
index 0000000..03166a2
--- /dev/null
+++ b/lib/dns/rdata/generic/talink_58.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* http://www.iana.org/assignments/dns-parameters/TALINK/talink-completed-template */
+
+#ifndef GENERIC_TALINK_58_H
+#define GENERIC_TALINK_58_H 1
+
+typedef struct dns_rdata_talink {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ dns_name_t prev;
+ dns_name_t next;
+} dns_rdata_talink_t;
+
+#endif /* GENERIC_TALINK_58_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..7bbd1d3
--- /dev/null
+++ b/lib/dns/rdata/generic/tkey_249.c
@@ -0,0 +1,556 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* 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 == dns_rdatatype_tkey);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ /*
+ * Algorithm.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ false));
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ if (origin == NULL)
+ origin = dns_rootname;
+ RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target));
+
+
+ /*
+ * Inception.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ false));
+ RETERR(uint32_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * Expiration.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ false));
+ RETERR(uint32_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * Mode.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ 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,
+ 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,
+ 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,
+ 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;
+ bool sub;
+
+ REQUIRE(rdata->type == dns_rdatatype_tkey);
+ 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);
+ snprintf(buf, sizeof(buf), "%lu ", n);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * Expiration.
+ */
+ n = uint32_fromregion(&sr);
+ isc_region_consume(&sr, 4);
+ snprintf(buf, sizeof(buf), "%lu ", n);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * Mode.
+ */
+ n = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+ snprintf(buf, sizeof(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 {
+ snprintf(buf, sizeof(buf), "%lu ", n);
+ RETERR(str_totext(buf, target));
+ }
+
+ /*
+ * Key Size.
+ */
+ n = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+ snprintf(buf, sizeof(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));
+ if (tctx->width == 0) /* No splitting */
+ RETERR(isc_base64_totext(&dr, 60, "", target));
+ else
+ 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);
+ snprintf(buf, sizeof(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));
+ if (tctx->width == 0) /* No splitting */
+ RETERR(isc_base64_totext(&dr, 60, "", target));
+ else
+ 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 == dns_rdatatype_tkey);
+
+ 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 == dns_rdatatype_tkey);
+ 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 == dns_rdatatype_tkey);
+ 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 == dns_rdatatype_tkey);
+ 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 == dns_rdatatype_tkey);
+ 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.
+ */
+ INSIST(tkey->keylen + 2U <= sr.length);
+ 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.
+ */
+ INSIST(tkey->otherlen <= sr.length);
+ 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 == dns_rdatatype_tkey);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_tkey(ARGS_DIGEST) {
+ UNUSED(rdata);
+ UNUSED(digest);
+ UNUSED(arg);
+
+ REQUIRE(rdata->type == dns_rdatatype_tkey);
+
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static inline bool
+checkowner_tkey(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_tkey);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_tkey(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_tkey);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_tkey(ARGS_COMPARE) {
+ return (compare_tkey(rdata1, rdata2));
+}
+#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..2fab087
--- /dev/null
+++ b/lib/dns/rdata/generic/tkey_249.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef GENERIC_TKEY_249_H
+#define GENERIC_TKEY_249_H 1
+
+
+/*!
+ * \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;
+ uint32_t inception;
+ uint32_t expire;
+ uint16_t mode;
+ uint16_t error;
+ uint16_t keylen;
+ unsigned char * key;
+ uint16_t otherlen;
+ unsigned char * other;
+} dns_rdata_tkey_t;
+
+
+#endif /* GENERIC_TKEY_249_H */
diff --git a/lib/dns/rdata/generic/tlsa_52.c b/lib/dns/rdata/generic/tlsa_52.c
new file mode 100644
index 0000000..1224dc1
--- /dev/null
+++ b/lib/dns/rdata/generic/tlsa_52.c
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* rfc6698.txt */
+
+#ifndef RDATA_GENERIC_TLSA_52_C
+#define RDATA_GENERIC_TLSA_52_C
+
+#define RRTYPE_TLSA_ATTRIBUTES 0
+
+static inline isc_result_t
+generic_fromtext_tlsa(ARGS_FROMTEXT) {
+ isc_token_t token;
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(callbacks);
+
+ /*
+ * Certificate Usage.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ false));
+ if (token.value.as_ulong > 0xffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint8_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * Selector.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ false));
+ if (token.value.as_ulong > 0xffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint8_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * Matching type.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ false));
+ if (token.value.as_ulong > 0xffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint8_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * Certificate Association Data.
+ */
+ return (isc_hex_tobuffer(lexer, target, -1));
+}
+
+static inline isc_result_t
+generic_totext_tlsa(ARGS_TOTEXT) {
+ isc_region_t sr;
+ char buf[sizeof("64000 ")];
+ unsigned int n;
+
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(tctx);
+
+ dns_rdata_toregion(rdata, &sr);
+
+ /*
+ * Certificate Usage.
+ */
+ n = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+ snprintf(buf, sizeof(buf), "%u ", n);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * Selector.
+ */
+ n = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+ snprintf(buf, sizeof(buf), "%u ", n);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * Matching type.
+ */
+ n = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+ snprintf(buf, sizeof(buf), "%u", n);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * Certificate Association Data.
+ */
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext(" (", target));
+ RETERR(str_totext(tctx->linebreak, target));
+ if (tctx->width == 0) /* No splitting */
+ RETERR(isc_hex_totext(&sr, 0, "", target));
+ else
+ 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
+generic_fromwire_tlsa(ARGS_FROMWIRE) {
+ isc_region_t sr;
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(dctx);
+ UNUSED(options);
+
+ isc_buffer_activeregion(source, &sr);
+
+ if (sr.length < 3)
+ return (ISC_R_UNEXPECTEDEND);
+
+ isc_buffer_forward(source, sr.length);
+ return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline isc_result_t
+fromtext_tlsa(ARGS_FROMTEXT) {
+
+ REQUIRE(type == dns_rdatatype_tlsa);
+
+ return (generic_fromtext_tlsa(rdclass, type, lexer, origin, options,
+ target, callbacks));
+}
+
+static inline isc_result_t
+totext_tlsa(ARGS_TOTEXT) {
+
+ REQUIRE(rdata->type == dns_rdatatype_tlsa);
+
+ return (generic_totext_tlsa(rdata, tctx, target));
+}
+
+static inline isc_result_t
+fromwire_tlsa(ARGS_FROMWIRE) {
+
+ REQUIRE(type == dns_rdatatype_tlsa);
+
+ return (generic_fromwire_tlsa(rdclass, type, source, dctx, options,
+ target));
+}
+
+static inline isc_result_t
+towire_tlsa(ARGS_TOWIRE) {
+ isc_region_t sr;
+
+ REQUIRE(rdata->type == dns_rdatatype_tlsa);
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(cctx);
+
+ dns_rdata_toregion(rdata, &sr);
+ return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline int
+compare_tlsa(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == dns_rdatatype_tlsa);
+ 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
+generic_fromstruct_tlsa(ARGS_FROMSTRUCT) {
+ dns_rdata_tlsa_t *tlsa = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(tlsa->common.rdtype == type);
+ REQUIRE(tlsa->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ RETERR(uint8_tobuffer(tlsa->usage, target));
+ RETERR(uint8_tobuffer(tlsa->selector, target));
+ RETERR(uint8_tobuffer(tlsa->match, target));
+
+ return (mem_tobuffer(target, tlsa->data, tlsa->length));
+}
+
+static inline isc_result_t
+generic_tostruct_tlsa(ARGS_TOSTRUCT) {
+ dns_rdata_tlsa_t *tlsa = target;
+ isc_region_t region;
+
+ REQUIRE(rdata != NULL);
+ REQUIRE(rdata->length != 0);
+
+ REQUIRE(tlsa != NULL);
+ REQUIRE(tlsa->common.rdclass == rdata->rdclass);
+ REQUIRE(tlsa->common.rdtype == rdata->type);
+ REQUIRE(!ISC_LINK_LINKED(&tlsa->common, link));
+
+ dns_rdata_toregion(rdata, &region);
+
+ tlsa->usage = uint8_fromregion(&region);
+ isc_region_consume(&region, 1);
+ tlsa->selector = uint8_fromregion(&region);
+ isc_region_consume(&region, 1);
+ tlsa->match = uint8_fromregion(&region);
+ isc_region_consume(&region, 1);
+ tlsa->length = region.length;
+
+ tlsa->data = mem_maybedup(mctx, region.base, region.length);
+ if (tlsa->data == NULL)
+ return (ISC_R_NOMEMORY);
+
+ tlsa->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+generic_freestruct_tlsa(ARGS_FREESTRUCT) {
+ dns_rdata_tlsa_t *tlsa = source;
+
+ REQUIRE(tlsa != NULL);
+
+ if (tlsa->mctx == NULL)
+ return;
+
+ if (tlsa->data != NULL)
+ isc_mem_free(tlsa->mctx, tlsa->data);
+ tlsa->mctx = NULL;
+}
+
+static inline isc_result_t
+fromstruct_tlsa(ARGS_FROMSTRUCT) {
+
+ REQUIRE(type == dns_rdatatype_tlsa);
+
+ return (generic_fromstruct_tlsa(rdclass, type, source, target));
+}
+
+static inline isc_result_t
+tostruct_tlsa(ARGS_TOSTRUCT) {
+ dns_rdata_tlsa_t *tlsa = target;
+
+ REQUIRE(rdata->type == dns_rdatatype_tlsa);
+ REQUIRE(target != NULL);
+
+ tlsa->common.rdclass = rdata->rdclass;
+ tlsa->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&tlsa->common, link);
+
+ return (generic_tostruct_tlsa(rdata, target, mctx));
+}
+
+static inline void
+freestruct_tlsa(ARGS_FREESTRUCT) {
+ dns_rdata_tlsa_t *tlsa = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(tlsa->common.rdtype == dns_rdatatype_tlsa);
+
+ generic_freestruct_tlsa(source);
+}
+
+static inline isc_result_t
+additionaldata_tlsa(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == dns_rdatatype_tlsa);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_tlsa(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == dns_rdatatype_tlsa);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_tlsa(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_tlsa);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_tlsa(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_tlsa);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_tlsa(ARGS_COMPARE) {
+ return (compare_tlsa(rdata1, rdata2));
+}
+
+#endif /* RDATA_GENERIC_TLSA_52_C */
diff --git a/lib/dns/rdata/generic/tlsa_52.h b/lib/dns/rdata/generic/tlsa_52.h
new file mode 100644
index 0000000..422fd8b
--- /dev/null
+++ b/lib/dns/rdata/generic/tlsa_52.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#ifndef GENERIC_TLSA_52_H
+#define GENERIC_TLSA_52_H 1
+
+/*!
+ * \brief per rfc6698.txt
+ */
+typedef struct dns_rdata_tlsa {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ uint8_t usage;
+ uint8_t selector;
+ uint8_t match;
+ uint16_t length;
+ unsigned char *data;
+} dns_rdata_tlsa_t;
+
+#endif /* GENERIC_TLSA_52_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..3f3842e
--- /dev/null
+++ b/lib/dns/rdata/generic/txt_16.c
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef RDATA_GENERIC_TXT_16_C
+#define RDATA_GENERIC_TXT_16_C
+
+#define RRTYPE_TXT_ATTRIBUTES (0)
+
+static inline isc_result_t
+generic_fromtext_txt(ARGS_FROMTEXT) {
+ isc_token_t token;
+ int strings;
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(callbacks);
+
+ strings = 0;
+ if ((options & DNS_RDATA_UNKNOWNESCAPE) != 0) {
+ isc_textregion_t r;
+ DE_CONST("#", r.base);
+ r.length = 1;
+ RETERR(txt_fromtext(&r, target));
+ strings++;
+ }
+ for (;;) {
+ RETERR(isc_lex_getmastertoken(lexer, &token,
+ isc_tokentype_qstring,
+ 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
+generic_totext_txt(ARGS_TOTEXT) {
+ isc_region_t region;
+
+ UNUSED(tctx);
+
+ dns_rdata_toregion(rdata, &region);
+
+ while (region.length > 0) {
+ RETERR(txt_totext(&region, true, target));
+ if (region.length > 0)
+ RETERR(str_totext(" ", target));
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+generic_fromwire_txt(ARGS_FROMWIRE) {
+ isc_result_t result;
+
+ 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
+fromtext_txt(ARGS_FROMTEXT) {
+
+ REQUIRE(type == dns_rdatatype_txt);
+
+ return (generic_fromtext_txt(rdclass, type, lexer, origin, options,
+ target, callbacks));
+}
+
+static inline isc_result_t
+totext_txt(ARGS_TOTEXT) {
+
+ REQUIRE(rdata->type == dns_rdatatype_txt);
+
+ return (generic_totext_txt(rdata, tctx, target));
+}
+
+static inline isc_result_t
+fromwire_txt(ARGS_FROMWIRE) {
+
+ REQUIRE(type == dns_rdatatype_txt);
+
+ return (generic_fromwire_txt(rdclass, type, source, dctx, options,
+ target));
+}
+
+static inline isc_result_t
+towire_txt(ARGS_TOWIRE) {
+
+ REQUIRE(rdata->type == dns_rdatatype_txt);
+
+ UNUSED(cctx);
+
+ return (mem_tobuffer(target, rdata->data, rdata->length));
+}
+
+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 == dns_rdatatype_txt);
+
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+ return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+generic_fromstruct_txt(ARGS_FROMSTRUCT) {
+ dns_rdata_txt_t *txt = source;
+ isc_region_t region;
+ uint8_t length;
+
+ 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
+generic_tostruct_txt(ARGS_TOSTRUCT) {
+ dns_rdata_txt_t *txt = target;
+ isc_region_t r;
+
+ REQUIRE(target != NULL);
+ REQUIRE(txt->common.rdclass == rdata->rdclass);
+ REQUIRE(txt->common.rdtype == rdata->type);
+ REQUIRE(!ISC_LINK_LINKED(&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
+generic_freestruct_txt(ARGS_FREESTRUCT) {
+ dns_rdata_txt_t *txt = source;
+
+ REQUIRE(source != NULL);
+
+ if (txt->mctx == NULL)
+ return;
+
+ if (txt->txt != NULL)
+ isc_mem_free(txt->mctx, txt->txt);
+ txt->mctx = NULL;
+}
+
+static inline isc_result_t
+fromstruct_txt(ARGS_FROMSTRUCT) {
+
+ REQUIRE(type == dns_rdatatype_txt);
+
+ return (generic_fromstruct_txt(rdclass, type, source, target));
+}
+
+static inline isc_result_t
+tostruct_txt(ARGS_TOSTRUCT) {
+ dns_rdata_txt_t *txt = target;
+
+ REQUIRE(rdata->type == dns_rdatatype_txt);
+ REQUIRE(target != NULL);
+
+ txt->common.rdclass = rdata->rdclass;
+ txt->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&txt->common, link);
+
+ return (generic_tostruct_txt(rdata, target, mctx));
+}
+
+static inline void
+freestruct_txt(ARGS_FREESTRUCT) {
+ dns_rdata_txt_t *txt = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(txt->common.rdtype == dns_rdatatype_txt);
+
+ generic_freestruct_txt(source);
+}
+
+static inline isc_result_t
+additionaldata_txt(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == dns_rdatatype_txt);
+
+ 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 == dns_rdatatype_txt);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_txt(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_txt);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_txt(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_txt);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_txt(ARGS_COMPARE) {
+ return (compare_txt(rdata1, rdata2));
+}
+
+static isc_result_t
+generic_txt_first(dns_rdata_txt_t *txt) {
+
+ REQUIRE(txt != NULL);
+ REQUIRE(txt->txt != NULL || txt->txt_len == 0);
+
+ if (txt->txt_len == 0)
+ return (ISC_R_NOMORE);
+
+ txt->offset = 0;
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+generic_txt_next(dns_rdata_txt_t *txt) {
+ isc_region_t r;
+ uint8_t length;
+
+ REQUIRE(txt != NULL);
+ REQUIRE(txt->txt != NULL && txt->txt_len != 0);
+
+ INSIST(txt->offset + 1 <= txt->txt_len);
+ r.base = txt->txt + txt->offset;
+ r.length = txt->txt_len - txt->offset;
+ length = uint8_fromregion(&r);
+ INSIST(txt->offset + 1 + length <= txt->txt_len);
+ txt->offset = txt->offset + 1 + length;
+ if (txt->offset == txt->txt_len)
+ return (ISC_R_NOMORE);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+generic_txt_current(dns_rdata_txt_t *txt, dns_rdata_txt_string_t *string) {
+ isc_region_t r;
+
+ REQUIRE(txt != NULL);
+ REQUIRE(string != NULL);
+ REQUIRE(txt->txt != NULL);
+ REQUIRE(txt->offset < txt->txt_len);
+
+ INSIST(txt->offset + 1 <= txt->txt_len);
+ r.base = txt->txt + txt->offset;
+ r.length = txt->txt_len - txt->offset;
+
+ string->length = uint8_fromregion(&r);
+ isc_region_consume(&r, 1);
+ string->data = r.base;
+ INSIST(txt->offset + 1 + string->length <= txt->txt_len);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_rdata_txt_first(dns_rdata_txt_t *txt) {
+
+ REQUIRE(txt != NULL);
+ REQUIRE(txt->common.rdtype == dns_rdatatype_txt);
+
+ return (generic_txt_first(txt));
+}
+
+isc_result_t
+dns_rdata_txt_next(dns_rdata_txt_t *txt) {
+
+ REQUIRE(txt != NULL);
+ REQUIRE(txt->common.rdtype == dns_rdatatype_txt);
+
+ return (generic_txt_next(txt));
+}
+
+isc_result_t
+dns_rdata_txt_current(dns_rdata_txt_t *txt, dns_rdata_txt_string_t *string) {
+
+ REQUIRE(txt != NULL);
+ REQUIRE(txt->common.rdtype == dns_rdatatype_txt);
+
+ return (generic_txt_current(txt, string));
+}
+#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..aaf94bf
--- /dev/null
+++ b/lib/dns/rdata/generic/txt_16.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* */
+#ifndef GENERIC_TXT_16_H
+#define GENERIC_TXT_16_H 1
+
+
+typedef struct dns_rdata_txt_string {
+ 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;
+ uint16_t txt_len;
+ /* private */
+ 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..1c4654f
--- /dev/null
+++ b/lib/dns/rdata/generic/unspec_103.c
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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 == dns_rdatatype_unspec);
+
+ 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 == dns_rdatatype_unspec);
+
+ 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 == dns_rdatatype_unspec);
+
+ 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 == dns_rdatatype_unspec);
+
+ 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 == dns_rdatatype_unspec);
+
+ 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 == dns_rdatatype_unspec);
+ 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 == dns_rdatatype_unspec);
+ 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 == dns_rdatatype_unspec);
+
+ 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 == dns_rdatatype_unspec);
+
+ 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 == dns_rdatatype_unspec);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_unspec(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_unspec);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_unspec(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_unspec);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_unspec(ARGS_COMPARE) {
+ return (compare_unspec(rdata1, rdata2));
+}
+
+#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..9afb1bd
--- /dev/null
+++ b/lib/dns/rdata/generic/unspec_103.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* */
+#ifndef GENERIC_UNSPEC_103_H
+#define GENERIC_UNSPEC_103_H 1
+
+
+typedef struct dns_rdata_unspec_t {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ unsigned char *data;
+ uint16_t datalen;
+} dns_rdata_unspec_t;
+
+#endif /* GENERIC_UNSPEC_103_H */
diff --git a/lib/dns/rdata/generic/uri_256.c b/lib/dns/rdata/generic/uri_256.c
new file mode 100644
index 0000000..8fb0307
--- /dev/null
+++ b/lib/dns/rdata/generic/uri_256.c
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#ifndef GENERIC_URI_256_C
+#define GENERIC_URI_256_C 1
+
+#define RRTYPE_URI_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_uri(ARGS_FROMTEXT) {
+ isc_token_t token;
+
+ REQUIRE(type == dns_rdatatype_uri);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(callbacks);
+
+ /*
+ * Priority
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ 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,
+ false));
+ if (token.value.as_ulong > 0xffffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint16_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * Target URI
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token,
+ isc_tokentype_qstring, false));
+ if (token.type != isc_tokentype_qstring)
+ RETTOK(DNS_R_SYNTAX);
+ RETTOK(multitxt_fromtext(&token.value.as_textregion, target));
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_uri(ARGS_TOTEXT) {
+ isc_region_t region;
+ unsigned short priority, weight;
+ char buf[sizeof("65000 ")];
+
+ UNUSED(tctx);
+
+ REQUIRE(rdata->type == dns_rdatatype_uri);
+ REQUIRE(rdata->length != 0);
+
+ dns_rdata_toregion(rdata, &region);
+
+ /*
+ * Priority
+ */
+ priority = uint16_fromregion(&region);
+ isc_region_consume(&region, 2);
+ snprintf(buf, sizeof(buf), "%u ", priority);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * Weight
+ */
+ weight = uint16_fromregion(&region);
+ isc_region_consume(&region, 2);
+ snprintf(buf, sizeof(buf), "%u ", weight);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * Target URI
+ */
+ RETERR(multitxt_totext(&region, target));
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+fromwire_uri(ARGS_FROMWIRE) {
+ isc_region_t region;
+
+ REQUIRE(type == dns_rdatatype_uri);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(dctx);
+ UNUSED(options);
+
+ /*
+ * Priority, weight
+ */
+ isc_buffer_activeregion(source, &region);
+ if (region.length < 4)
+ return (ISC_R_UNEXPECTEDEND);
+
+ /*
+ * Priority, weight and target URI
+ */
+ isc_buffer_forward(source, region.length);
+ return (mem_tobuffer(target, region.base, region.length));
+}
+
+static inline isc_result_t
+towire_uri(ARGS_TOWIRE) {
+ isc_region_t region;
+
+ REQUIRE(rdata->type == dns_rdatatype_uri);
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(cctx);
+
+ dns_rdata_toregion(rdata, &region);
+ return (mem_tobuffer(target, region.base, region.length));
+}
+
+static inline int
+compare_uri(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+ int order;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == dns_rdatatype_uri);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+
+ /*
+ * Priority
+ */
+ order = memcmp(r1.base, r2.base, 2);
+ if (order != 0)
+ return (order < 0 ? -1 : 1);
+ isc_region_consume(&r1, 2);
+ isc_region_consume(&r2, 2);
+
+ /*
+ * Weight
+ */
+ order = memcmp(r1.base, r2.base, 2);
+ if (order != 0)
+ return (order < 0 ? -1 : 1);
+ isc_region_consume(&r1, 2);
+ isc_region_consume(&r2, 2);
+
+ return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+fromstruct_uri(ARGS_FROMSTRUCT) {
+ dns_rdata_uri_t *uri = source;
+
+ REQUIRE(type == dns_rdatatype_uri);
+ REQUIRE(source != NULL);
+ REQUIRE(uri->common.rdtype == type);
+ REQUIRE(uri->common.rdclass == rdclass);
+ REQUIRE(uri->target != NULL && uri->tgt_len != 0);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ /*
+ * Priority
+ */
+ RETERR(uint16_tobuffer(uri->priority, target));
+
+ /*
+ * Weight
+ */
+ RETERR(uint16_tobuffer(uri->weight, target));
+
+ /*
+ * Target URI
+ */
+ return (mem_tobuffer(target, uri->target, uri->tgt_len));
+}
+
+static inline isc_result_t
+tostruct_uri(ARGS_TOSTRUCT) {
+ dns_rdata_uri_t *uri = target;
+ isc_region_t sr;
+
+ REQUIRE(rdata->type == dns_rdatatype_uri);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ uri->common.rdclass = rdata->rdclass;
+ uri->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&uri->common, link);
+
+ dns_rdata_toregion(rdata, &sr);
+
+ /*
+ * Priority
+ */
+ if (sr.length < 2)
+ return (ISC_R_UNEXPECTEDEND);
+ uri->priority = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+
+ /*
+ * Weight
+ */
+ if (sr.length < 2)
+ return (ISC_R_UNEXPECTEDEND);
+ uri->weight = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+
+ /*
+ * Target URI
+ */
+ uri->tgt_len = sr.length;
+ uri->target = mem_maybedup(mctx, sr.base, sr.length);
+ if (uri->target == NULL)
+ return (ISC_R_NOMEMORY);
+
+ uri->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_uri(ARGS_FREESTRUCT) {
+ dns_rdata_uri_t *uri = (dns_rdata_uri_t *) source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(uri->common.rdtype == dns_rdatatype_uri);
+
+ if (uri->mctx == NULL)
+ return;
+
+ if (uri->target != NULL)
+ isc_mem_free(uri->mctx, uri->target);
+ uri->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_uri(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == dns_rdatatype_uri);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_uri(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == dns_rdatatype_uri);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_uri(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_uri);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_uri(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_uri);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_uri(ARGS_COMPARE) {
+ return (compare_uri(rdata1, rdata2));
+}
+
+#endif /* GENERIC_URI_256_C */
diff --git a/lib/dns/rdata/generic/uri_256.h b/lib/dns/rdata/generic/uri_256.h
new file mode 100644
index 0000000..cea1d22
--- /dev/null
+++ b/lib/dns/rdata/generic/uri_256.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef GENERIC_URI_256_H
+#define GENERIC_URI_256_H 1
+
+
+typedef struct dns_rdata_uri {
+ dns_rdatacommon_t common;
+ isc_mem_t * mctx;
+ uint16_t priority;
+ uint16_t weight;
+ unsigned char * target;
+ uint16_t tgt_len;
+} dns_rdata_uri_t;
+
+#endif /* GENERIC_URI_256_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..d267f65
--- /dev/null
+++ b/lib/dns/rdata/generic/x25_19.c
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* 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 == dns_rdatatype_x25);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(callbacks);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_qstring,
+ 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 == dns_rdatatype_x25);
+ REQUIRE(rdata->length != 0);
+
+ dns_rdata_toregion(rdata, &region);
+ return (txt_totext(&region, true, target));
+}
+
+static inline isc_result_t
+fromwire_x25(ARGS_FROMWIRE) {
+ isc_region_t sr;
+
+ REQUIRE(type == dns_rdatatype_x25);
+
+ 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 == dns_rdatatype_x25);
+ 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 == dns_rdatatype_x25);
+ 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;
+ uint8_t i;
+
+ REQUIRE(type == dns_rdatatype_x25);
+ 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 == dns_rdatatype_x25);
+ 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 == dns_rdatatype_x25);
+
+ 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 == dns_rdatatype_x25);
+
+ 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 == dns_rdatatype_x25);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_x25(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_x25);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_x25(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_x25);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_x25(ARGS_COMPARE) {
+ return (compare_x25(rdata1, rdata2));
+}
+
+#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..b10063c
--- /dev/null
+++ b/lib/dns/rdata/generic/x25_19.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef GENERIC_X25_19_H
+#define GENERIC_X25_19_H 1
+
+
+/*!
+ * \brief Per RFC1183 */
+
+typedef struct dns_rdata_x25 {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ unsigned char *x25;
+ 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..973465e
--- /dev/null
+++ b/lib/dns/rdata/hs_4/a_1.c
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#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 == dns_rdatatype_a);
+ REQUIRE(rdclass == dns_rdataclass_hs);
+
+ UNUSED(type);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(rdclass);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ 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);
+ memmove(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 == dns_rdatatype_a);
+ REQUIRE(rdata->rdclass == dns_rdataclass_hs);
+ 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 == dns_rdatatype_a);
+ REQUIRE(rdclass == dns_rdataclass_hs);
+
+ 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);
+
+ memmove(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 == dns_rdatatype_a);
+ REQUIRE(rdata->rdclass == dns_rdataclass_hs);
+ REQUIRE(rdata->length == 4);
+
+ UNUSED(cctx);
+
+ isc_buffer_availableregion(target, &region);
+ if (region.length < rdata->length)
+ return (ISC_R_NOSPACE);
+ memmove(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 == dns_rdatatype_a);
+ REQUIRE(rdata1->rdclass == dns_rdataclass_hs);
+ 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;
+ uint32_t n;
+
+ REQUIRE(type == dns_rdatatype_a);
+ REQUIRE(rdclass == dns_rdataclass_hs);
+ 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;
+ uint32_t n;
+ isc_region_t region;
+
+ REQUIRE(rdata->type == dns_rdatatype_a);
+ REQUIRE(rdata->rdclass == dns_rdataclass_hs);
+ 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 == dns_rdatatype_a);
+ REQUIRE(rdata->rdclass == dns_rdataclass_hs);
+
+ 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 == dns_rdatatype_a);
+ REQUIRE(rdata->rdclass == dns_rdataclass_hs);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_hs_a(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_a);
+ REQUIRE(rdclass == dns_rdataclass_hs);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_hs_a(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_a);
+ REQUIRE(rdata->rdclass == dns_rdataclass_hs);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_hs_a(ARGS_COMPARE) {
+ return (compare_hs_a(rdata1, rdata2));
+}
+
+#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..c5f6996
--- /dev/null
+++ b/lib/dns/rdata/hs_4/a_1.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* */
+#ifndef HS_4_A_1_H
+#define HS_4_A_1_H 1
+
+
+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..9d7d431
--- /dev/null
+++ b/lib/dns/rdata/in_1/a6_38.c
@@ -0,0 +1,460 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/* 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;
+ bool ok;
+
+ REQUIRE(type == dns_rdatatype_a6);
+ REQUIRE(rdclass == dns_rdataclass_in);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ /*
+ * Prefix length.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ 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,
+ 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,
+ false));
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ if (origin == NULL)
+ origin = dns_rootname;
+ RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target));
+ ok = true;
+ if ((options & DNS_RDATA_CHECKNAMES) != 0)
+ ok = dns_name_ishostname(&name, 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;
+ bool sub;
+
+ REQUIRE(rdata->type == dns_rdatatype_a6);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+ REQUIRE(rdata->length != 0);
+
+ dns_rdata_toregion(rdata, &sr);
+ prefixlen = sr.base[0];
+ INSIST(prefixlen <= 128);
+ isc_region_consume(&sr, 1);
+ snprintf(buf, sizeof(buf), "%u", prefixlen);
+ RETERR(str_totext(buf, target));
+ RETERR(str_totext(" ", target));
+
+ if (prefixlen != 128) {
+ octets = prefixlen/8;
+ memset(addr, 0, sizeof(addr));
+ memmove(&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 == dns_rdatatype_a6);
+ REQUIRE(rdclass == dns_rdataclass_in);
+
+ 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 == dns_rdatatype_a6);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+ 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 == dns_rdatatype_a6);
+ REQUIRE(rdata1->rdclass == dns_rdataclass_in);
+ 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;
+ uint8_t bits;
+ uint8_t first;
+ uint8_t mask;
+
+ REQUIRE(type == dns_rdatatype_a6);
+ REQUIRE(rdclass == dns_rdataclass_in);
+ 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 == dns_rdatatype_a6);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+ 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);
+ memmove(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 == dns_rdataclass_in);
+ REQUIRE(a6->common.rdtype == dns_rdatatype_a6);
+
+ 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 == dns_rdatatype_a6);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+
+ 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 == dns_rdatatype_a6);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+
+ 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 bool
+checkowner_in_a6(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_a6);
+ REQUIRE(rdclass == dns_rdataclass_in);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ return (dns_name_ishostname(name, wildcard));
+}
+
+static inline bool
+checknames_in_a6(ARGS_CHECKNAMES) {
+ isc_region_t region;
+ dns_name_t name;
+ unsigned int prefixlen;
+
+ REQUIRE(rdata->type == dns_rdatatype_a6);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+
+ UNUSED(owner);
+
+ dns_rdata_toregion(rdata, &region);
+ prefixlen = uint8_fromregion(&region);
+ if (prefixlen == 0)
+ return (true);
+ isc_region_consume(&region, 1 + 16 - prefixlen / 8);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &region);
+ if (!dns_name_ishostname(&name, false)) {
+ if (bad != NULL)
+ dns_name_clone(&name, bad);
+ return (false);
+ }
+ return (true);
+}
+
+static inline int
+casecompare_in_a6(ARGS_COMPARE) {
+ return (compare_in_a6(rdata1, rdata2));
+}
+
+#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..9811640
--- /dev/null
+++ b/lib/dns/rdata/in_1/a6_38.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef IN_1_A6_38_H
+#define IN_1_A6_38_H 1
+
+
+/*!
+ * \brief Per RFC2874 */
+
+typedef struct dns_rdata_in_a6 {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ dns_name_t prefix;
+ 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..d277699
--- /dev/null
+++ b/lib/dns/rdata/in_1/a_1.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#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 == dns_rdatatype_a);
+ REQUIRE(rdclass == dns_rdataclass_in);
+
+ UNUSED(type);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(rdclass);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ 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);
+ memmove(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 == dns_rdatatype_a);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+ 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 == dns_rdatatype_a);
+ REQUIRE(rdclass == dns_rdataclass_in);
+
+ 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);
+
+ memmove(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 == dns_rdatatype_a);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+ REQUIRE(rdata->length == 4);
+
+ UNUSED(cctx);
+
+ isc_buffer_availableregion(target, &region);
+ if (region.length < rdata->length)
+ return (ISC_R_NOSPACE);
+ memmove(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 == dns_rdatatype_a);
+ REQUIRE(rdata1->rdclass == dns_rdataclass_in);
+ 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;
+ uint32_t n;
+
+ REQUIRE(type == dns_rdatatype_a);
+ REQUIRE(rdclass == dns_rdataclass_in);
+ 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;
+ uint32_t n;
+ isc_region_t region;
+
+ REQUIRE(rdata->type == dns_rdatatype_a);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+ 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 == dns_rdatatype_a);
+ REQUIRE(a->common.rdclass == dns_rdataclass_in);
+
+ UNUSED(a);
+}
+
+static inline isc_result_t
+additionaldata_in_a(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == dns_rdatatype_a);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+
+ 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 == dns_rdatatype_a);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_in_a(ARGS_CHECKOWNER) {
+ dns_name_t prefix, suffix;
+
+ REQUIRE(type == dns_rdatatype_a);
+ REQUIRE(rdclass == dns_rdataclass_in);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ /*
+ * Handle Active Diretory gc._msdcs.<forest> name.
+ */
+ if (dns_name_countlabels(name) > 2U) {
+ dns_name_init(&prefix, NULL);
+ dns_name_init(&suffix, NULL);
+ dns_name_split(name, dns_name_countlabels(name) - 2,
+ &prefix, &suffix);
+ if (dns_name_equal(&gc_msdcs, &prefix) &&
+ dns_name_ishostname(&suffix, false))
+ return (true);
+ }
+
+ return (dns_name_ishostname(name, wildcard));
+}
+
+static inline bool
+checknames_in_a(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_a);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_in_a(ARGS_COMPARE) {
+ return (compare_in_a(rdata1, rdata2));
+}
+
+#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..f44809d
--- /dev/null
+++ b/lib/dns/rdata/in_1/a_1.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* */
+#ifndef IN_1_A_1_H
+#define IN_1_A_1_H 1
+
+
+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..655c5ed
--- /dev/null
+++ b/lib/dns/rdata/in_1/aaaa_28.c
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* 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 == dns_rdatatype_aaaa);
+ REQUIRE(rdclass == dns_rdataclass_in);
+
+ UNUSED(type);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ 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);
+ memmove(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 == dns_rdatatype_aaaa);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+ 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 == dns_rdatatype_aaaa);
+ REQUIRE(rdclass == dns_rdataclass_in);
+
+ 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);
+
+ memmove(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 == dns_rdatatype_aaaa);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+ REQUIRE(rdata->length == 16);
+
+ isc_buffer_availableregion(target, &region);
+ if (region.length < rdata->length)
+ return (ISC_R_NOSPACE);
+ memmove(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 == dns_rdatatype_aaaa);
+ REQUIRE(rdata1->rdclass == dns_rdataclass_in);
+ 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 == dns_rdatatype_aaaa);
+ REQUIRE(rdclass == dns_rdataclass_in);
+ 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 == dns_rdatatype_aaaa);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+ 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);
+ memmove(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 == dns_rdataclass_in);
+ REQUIRE(aaaa->common.rdtype == dns_rdatatype_aaaa);
+
+ UNUSED(aaaa);
+}
+
+static inline isc_result_t
+additionaldata_in_aaaa(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == dns_rdatatype_aaaa);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+
+ 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 == dns_rdatatype_aaaa);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_in_aaaa(ARGS_CHECKOWNER) {
+ dns_name_t prefix, suffix;
+
+ REQUIRE(type == dns_rdatatype_aaaa);
+ REQUIRE(rdclass == dns_rdataclass_in);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ /*
+ * Handle Active Diretory gc._msdcs.<forest> name.
+ */
+ if (dns_name_countlabels(name) > 2U) {
+ dns_name_init(&prefix, NULL);
+ dns_name_init(&suffix, NULL);
+ dns_name_split(name, dns_name_countlabels(name) - 2,
+ &prefix, &suffix);
+ if (dns_name_equal(&gc_msdcs, &prefix) &&
+ dns_name_ishostname(&suffix, false))
+ return (true);
+ }
+
+ return (dns_name_ishostname(name, wildcard));
+}
+
+static inline bool
+checknames_in_aaaa(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_aaaa);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_in_aaaa(ARGS_COMPARE) {
+ return (compare_in_aaaa(rdata1, rdata2));
+}
+#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..caabf7e
--- /dev/null
+++ b/lib/dns/rdata/in_1/aaaa_28.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef IN_1_AAAA_28_H
+#define IN_1_AAAA_28_H 1
+
+
+/*!
+ * \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..ae35db9
--- /dev/null
+++ b/lib/dns/rdata/in_1/apl_42.c
@@ -0,0 +1,456 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/* 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;
+ uint8_t prefix;
+ uint8_t len;
+ bool neg;
+ char *cp, *ap, *slash;
+ int n;
+
+ REQUIRE(type == dns_rdatatype_apl);
+ REQUIRE(rdclass == dns_rdataclass_in);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(callbacks);
+
+ do {
+ RETERR(isc_lex_getmastertoken(lexer, &token,
+ isc_tokentype_string, true));
+ if (token.type != isc_tokentype_string)
+ break;
+
+ cp = DNS_AS_STR(token);
+ neg = (*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;
+ uint16_t afi;
+ uint8_t prefix;
+ uint8_t len;
+ bool neg;
+ unsigned char buf[16];
+ char txt[sizeof(" !64000:")];
+ const char *sep = "";
+ int n;
+
+ REQUIRE(rdata->type == dns_rdatatype_apl);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+
+ 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 = (*sr.base & 0x80);
+ 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));
+ memmove(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));
+ memmove(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;
+ uint16_t afi;
+ uint8_t prefix;
+ uint8_t len;
+
+ REQUIRE(type == dns_rdatatype_apl);
+ REQUIRE(rdclass == dns_rdataclass_in);
+
+ 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 == dns_rdatatype_apl);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+
+ 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 == dns_rdatatype_apl);
+ REQUIRE(rdata1->rdclass == dns_rdataclass_in);
+
+ 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 == dns_rdatatype_apl);
+ REQUIRE(rdclass == dns_rdataclass_in);
+ 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, 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 == dns_rdatatype_apl);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+
+ 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 == dns_rdatatype_apl);
+ REQUIRE(apl->common.rdclass == dns_rdataclass_in);
+
+ 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) {
+ uint32_t length;
+
+ REQUIRE(apl != NULL);
+ REQUIRE(apl->common.rdtype == dns_rdatatype_apl);
+ REQUIRE(apl->common.rdclass == dns_rdataclass_in);
+ 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(4 + length <= apl->apl_len);
+
+ apl->offset = 0;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_rdata_apl_next(dns_rdata_in_apl_t *apl) {
+ uint32_t length;
+
+ REQUIRE(apl != NULL);
+ REQUIRE(apl->common.rdtype == dns_rdatatype_apl);
+ REQUIRE(apl->common.rdclass == dns_rdataclass_in);
+ 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(4 + length + apl->offset <= apl->apl_len);
+
+ apl->offset += 4 + length;
+ 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) {
+ uint32_t length;
+
+ REQUIRE(apl != NULL);
+ REQUIRE(apl->common.rdtype == dns_rdatatype_apl);
+ REQUIRE(apl->common.rdclass == dns_rdataclass_in);
+ 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(4 + 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 = length;
+ ent->negative = (apl->apl[apl->offset + 3] & 0x80);
+ if (ent->length != 0)
+ ent->data = &apl->apl[apl->offset + 4];
+ else
+ ent->data = NULL;
+ return (ISC_R_SUCCESS);
+}
+
+unsigned int
+dns_rdata_apl_count(const dns_rdata_in_apl_t *apl) {
+ return (apl->apl_len);
+}
+
+static inline isc_result_t
+additionaldata_in_apl(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == dns_rdatatype_apl);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+
+ (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 == dns_rdatatype_apl);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_in_apl(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_apl);
+ REQUIRE(rdclass == dns_rdataclass_in);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+
+static inline bool
+checknames_in_apl(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_apl);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_in_apl(ARGS_COMPARE) {
+ return (compare_in_apl(rdata1, rdata2));
+}
+
+#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..8c17a53
--- /dev/null
+++ b/lib/dns/rdata/in_1/apl_42.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* */
+#ifndef IN_1_APL_42_H
+#define IN_1_APL_42_H 1
+
+
+typedef struct dns_rdata_apl_ent {
+ bool negative;
+ uint16_t family;
+ uint8_t prefix;
+ 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;
+ uint16_t apl_len;
+ /* private */
+ 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 *);
+
+unsigned int
+dns_rdata_apl_count(const dns_rdata_in_apl_t *apl);
+
+#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..90971b7
--- /dev/null
+++ b/lib/dns/rdata/in_1/dhcid_49.c
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/* 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 == dns_rdatatype_dhcid);
+ REQUIRE(rdclass == dns_rdataclass_in);
+
+ 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, sr2;
+ char buf[sizeof(" ; 64000 255 64000")];
+ size_t n;
+
+ REQUIRE(rdata->type == dns_rdatatype_dhcid);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+ REQUIRE(rdata->length != 0);
+
+ dns_rdata_toregion(rdata, &sr);
+ sr2 = sr;
+
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext("( " /*)*/, target));
+ if (tctx->width == 0) /* No splitting */
+ RETERR(isc_base64_totext(&sr, 60, "", target));
+ else
+ 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",
+ sr2.base[0] * 256U + sr2.base[1],
+ sr2.base[2], rdata->length - 3U);
+ 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 == dns_rdatatype_dhcid);
+ REQUIRE(rdclass == dns_rdataclass_in);
+
+ 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 == dns_rdatatype_dhcid);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+ 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 == dns_rdatatype_dhcid);
+ REQUIRE(rdata1->rdclass == dns_rdataclass_in);
+ 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 == dns_rdatatype_dhcid);
+ REQUIRE(rdclass == dns_rdataclass_in);
+ 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 == dns_rdatatype_dhcid);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+ 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 == dns_rdatatype_dhcid);
+ REQUIRE(dhcid->common.rdclass == dns_rdataclass_in);
+
+ 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 == dns_rdatatype_dhcid);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+
+ 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 == dns_rdatatype_dhcid);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_in_dhcid(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_dhcid);
+ REQUIRE(rdclass == dns_rdataclass_in);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_in_dhcid(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_dhcid);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_in_dhcid(ARGS_COMPARE) {
+ return (compare_in_dhcid(rdata1, rdata2));
+}
+
+#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..455eaed
--- /dev/null
+++ b/lib/dns/rdata/in_1/dhcid_49.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* */
+#ifndef IN_1_DHCID_49_H
+#define IN_1_DHCID_49_H 1
+
+
+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..2786951
--- /dev/null
+++ b/lib/dns/rdata/in_1/kx_36.c
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* 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 == dns_rdatatype_kx);
+ REQUIRE(rdclass == dns_rdataclass_in);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ 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,
+ false));
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ if (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;
+ bool sub;
+ char buf[sizeof("64000")];
+ unsigned short num;
+
+ REQUIRE(rdata->type == dns_rdatatype_kx);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+ 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);
+ snprintf(buf, sizeof(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 == dns_rdatatype_kx);
+ REQUIRE(rdclass == dns_rdataclass_in);
+
+ 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 == dns_rdatatype_kx);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+ 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 == dns_rdatatype_kx);
+ REQUIRE(rdata1->rdclass == dns_rdataclass_in);
+ 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 == dns_rdatatype_kx);
+ REQUIRE(rdclass == dns_rdataclass_in);
+ 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 == dns_rdatatype_kx);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+ 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 == dns_rdataclass_in);
+ REQUIRE(kx->common.rdtype == dns_rdatatype_kx);
+
+ 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 == dns_rdatatype_kx);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+
+ 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 == dns_rdatatype_kx);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+
+ 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 bool
+checkowner_in_kx(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_kx);
+ REQUIRE(rdclass == dns_rdataclass_in);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_in_kx(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_kx);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_in_kx(ARGS_COMPARE) {
+ return (compare_in_kx(rdata1, rdata2));
+}
+
+#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..577a4c4
--- /dev/null
+++ b/lib/dns/rdata/in_1/kx_36.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef IN_1_KX_36_H
+#define IN_1_KX_36_H 1
+
+
+/*!
+ * \brief Per RFC2230 */
+
+typedef struct dns_rdata_in_kx {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ 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/nsap-ptr_23.c b/lib/dns/rdata/in_1/nsap-ptr_23.c
new file mode 100644
index 0000000..3217300
--- /dev/null
+++ b/lib/dns/rdata/in_1/nsap-ptr_23.c
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* 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 == dns_rdatatype_nsap_ptr);
+ REQUIRE(rdclass == dns_rdataclass_in);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ false));
+
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ if (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;
+ bool sub;
+
+ REQUIRE(rdata->type == dns_rdatatype_nsap_ptr);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+ 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 == dns_rdatatype_nsap_ptr);
+ REQUIRE(rdclass == dns_rdataclass_in);
+
+ 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 == dns_rdatatype_nsap_ptr);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+ 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 == dns_rdatatype_nsap_ptr);
+ REQUIRE(rdata1->rdclass == dns_rdataclass_in);
+ 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 == dns_rdatatype_nsap_ptr);
+ REQUIRE(rdclass == dns_rdataclass_in);
+ 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 == dns_rdatatype_nsap_ptr);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+ 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 == dns_rdataclass_in);
+ REQUIRE(nsap_ptr->common.rdtype == dns_rdatatype_nsap_ptr);
+
+ 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 == dns_rdatatype_nsap_ptr);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+
+ 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 == dns_rdatatype_nsap_ptr);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+
+ dns_rdata_toregion(rdata, &r);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &r);
+
+ return (dns_name_digest(&name, digest, arg));
+}
+
+static inline bool
+checkowner_in_nsap_ptr(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_nsap_ptr);
+ REQUIRE(rdclass == dns_rdataclass_in);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_in_nsap_ptr(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_nsap_ptr);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_in_nsap_ptr(ARGS_COMPARE) {
+ return (compare_in_nsap_ptr(rdata1, rdata2));
+}
+
+#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..4880a09
--- /dev/null
+++ b/lib/dns/rdata/in_1/nsap-ptr_23.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef IN_1_NSAP_PTR_23_H
+#define IN_1_NSAP_PTR_23_H 1
+
+
+/*!
+ * \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..ba3e69c
--- /dev/null
+++ b/lib/dns/rdata/in_1/nsap_22.c
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* 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;
+ bool valid = false;
+ int digits = 0;
+ unsigned char c = 0;
+
+ REQUIRE(type == dns_rdatatype_nsap);
+ REQUIRE(rdclass == dns_rdataclass_in);
+
+ UNUSED(type);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ /* 0x<hex.string.with.periods> */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ 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);
+ 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));
+ valid = true;
+ digits = 0;
+ c = 0;
+ }
+ isc_textregion_consume(sr, 1);
+ }
+ if (digits != 0 || !valid)
+ 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 == dns_rdatatype_nsap);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(tctx);
+
+ dns_rdata_toregion(rdata, &region);
+ RETERR(str_totext("0x", target));
+ while (region.length != 0) {
+ snprintf(buf, sizeof(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 == dns_rdatatype_nsap);
+ REQUIRE(rdclass == dns_rdataclass_in);
+
+ 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 == dns_rdatatype_nsap);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+ 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 == dns_rdatatype_nsap);
+ REQUIRE(rdata1->rdclass == dns_rdataclass_in);
+ 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 == dns_rdatatype_nsap);
+ REQUIRE(rdclass == dns_rdataclass_in);
+ 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 == dns_rdatatype_nsap);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+ 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 == dns_rdataclass_in);
+ REQUIRE(nsap->common.rdtype == dns_rdatatype_nsap);
+
+ 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 == dns_rdatatype_nsap);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+
+ 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 == dns_rdatatype_nsap);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_in_nsap(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_nsap);
+ REQUIRE(rdclass == dns_rdataclass_in);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_in_nsap(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_nsap);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_in_nsap(ARGS_COMPARE) {
+ return (compare_in_nsap(rdata1, rdata2));
+}
+
+#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..79599ef
--- /dev/null
+++ b/lib/dns/rdata/in_1/nsap_22.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef IN_1_NSAP_22_H
+#define IN_1_NSAP_22_H 1
+
+
+/*!
+ * \brief Per RFC1706 */
+
+typedef struct dns_rdata_in_nsap {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ unsigned char *nsap;
+ 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..a94b9df
--- /dev/null
+++ b/lib/dns/rdata/in_1/px_26.c
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* 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 == dns_rdatatype_px);
+ REQUIRE(rdclass == dns_rdataclass_in);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ if (origin == NULL)
+ origin = dns_rootname;
+
+ /*
+ * Preference.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ 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,
+ false));
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target));
+
+ /*
+ * MAPX400.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ false));
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ 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;
+ bool sub;
+ char buf[sizeof("64000")];
+ unsigned short num;
+
+ REQUIRE(rdata->type == dns_rdatatype_px);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+ 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);
+ snprintf(buf, sizeof(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 == dns_rdatatype_px);
+ REQUIRE(rdclass == dns_rdataclass_in);
+
+ 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 == dns_rdatatype_px);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+ 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 == dns_rdatatype_px);
+ REQUIRE(rdata1->rdclass == dns_rdataclass_in);
+ 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 == dns_rdatatype_px);
+ REQUIRE(rdclass == dns_rdataclass_in);
+ 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 == dns_rdatatype_px);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+ 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 == dns_rdataclass_in);
+ REQUIRE(px->common.rdtype == dns_rdatatype_px);
+
+ 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 == dns_rdatatype_px);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+
+ 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 == dns_rdatatype_px);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+
+ 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 bool
+checkowner_in_px(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_px);
+ REQUIRE(rdclass == dns_rdataclass_in);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_in_px(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_px);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_in_px(ARGS_COMPARE) {
+ return (compare_in_px(rdata1, rdata2));
+}
+
+#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..3cab1f1
--- /dev/null
+++ b/lib/dns/rdata/in_1/px_26.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef IN_1_PX_26_H
+#define IN_1_PX_26_H 1
+
+
+/*!
+ * \brief Per RFC2163 */
+
+typedef struct dns_rdata_in_px {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ 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..63a4e80
--- /dev/null
+++ b/lib/dns/rdata/in_1/srv_33.c
@@ -0,0 +1,394 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* 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;
+ bool ok;
+
+ REQUIRE(type == dns_rdatatype_srv);
+ REQUIRE(rdclass == dns_rdataclass_in);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ /*
+ * Priority.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ 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,
+ 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,
+ 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,
+ false));
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ if (origin == NULL)
+ origin = dns_rootname;
+ RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target));
+ ok = true;
+ if ((options & DNS_RDATA_CHECKNAMES) != 0)
+ ok = dns_name_ishostname(&name, 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;
+ bool sub;
+ char buf[sizeof("64000")];
+ unsigned short num;
+
+ REQUIRE(rdata->type == dns_rdatatype_srv);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+ 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);
+ snprintf(buf, sizeof(buf), "%u", num);
+ RETERR(str_totext(buf, target));
+ RETERR(str_totext(" ", target));
+
+ /*
+ * Weight.
+ */
+ num = uint16_fromregion(&region);
+ isc_region_consume(&region, 2);
+ snprintf(buf, sizeof(buf), "%u", num);
+ RETERR(str_totext(buf, target));
+ RETERR(str_totext(" ", target));
+
+ /*
+ * Port.
+ */
+ num = uint16_fromregion(&region);
+ isc_region_consume(&region, 2);
+ snprintf(buf, sizeof(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 == dns_rdatatype_srv);
+ REQUIRE(rdclass == dns_rdataclass_in);
+
+ 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 == dns_rdatatype_srv);
+ 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 == dns_rdatatype_srv);
+ REQUIRE(rdata1->rdclass == dns_rdataclass_in);
+ 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 == dns_rdatatype_srv);
+ REQUIRE(rdclass == dns_rdataclass_in);
+ 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 == dns_rdataclass_in);
+ REQUIRE(rdata->type == dns_rdatatype_srv);
+ 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 == dns_rdataclass_in);
+ REQUIRE(srv->common.rdtype == dns_rdatatype_srv);
+
+ 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) {
+ char buf[sizeof("_65000._tcp")];
+ dns_fixedname_t fixed;
+ dns_name_t name;
+ dns_offsets_t offsets;
+ isc_region_t region;
+ uint16_t port;
+ isc_result_t result;
+
+ REQUIRE(rdata->type == dns_rdatatype_srv);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+
+ dns_name_init(&name, offsets);
+ dns_rdata_toregion(rdata, &region);
+ isc_region_consume(&region, 4);
+ port = uint16_fromregion(&region);
+ isc_region_consume(&region, 2);
+ dns_name_fromregion(&name, &region);
+
+ if (dns_name_equal(&name, dns_rootname))
+ return (ISC_R_SUCCESS);
+
+ result = (add)(arg, &name, dns_rdatatype_a);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ dns_fixedname_init(&fixed);
+ snprintf(buf, sizeof(buf), "_%u._tcp", port);
+ result = dns_name_fromstring2(dns_fixedname_name(&fixed), buf, NULL,
+ 0, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (ISC_R_SUCCESS);
+
+ result = dns_name_concatenate(dns_fixedname_name(&fixed), &name,
+ dns_fixedname_name(&fixed), NULL);
+ if (result != ISC_R_SUCCESS)
+ return (ISC_R_SUCCESS);
+
+ return ((add)(arg, dns_fixedname_name(&fixed), dns_rdatatype_tlsa));
+}
+
+static inline isc_result_t
+digest_in_srv(ARGS_DIGEST) {
+ isc_region_t r1, r2;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == dns_rdatatype_srv);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+
+ 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 bool
+checkowner_in_srv(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_srv);
+ REQUIRE(rdclass == dns_rdataclass_in);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (true);
+}
+
+static inline bool
+checknames_in_srv(ARGS_CHECKNAMES) {
+ isc_region_t region;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == dns_rdatatype_srv);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+
+ 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, false)) {
+ if (bad != NULL)
+ dns_name_clone(&name, bad);
+ return (false);
+ }
+ return (true);
+}
+
+static inline int
+casecompare_in_srv(ARGS_COMPARE) {
+ return (compare_in_srv(rdata1, rdata2));
+}
+
+#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..78d5ba9
--- /dev/null
+++ b/lib/dns/rdata/in_1/srv_33.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef IN_1_SRV_33_H
+#define IN_1_SRV_33_H 1
+
+/*!
+ * \brief Per RFC2782 */
+
+typedef struct dns_rdata_in_srv {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ uint16_t priority;
+ uint16_t weight;
+ 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..0dc5804
--- /dev/null
+++ b/lib/dns/rdata/in_1/wks_11.c
@@ -0,0 +1,412 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#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>
+#include <isc/once.h>
+
+/*
+ * Redefine CHECK here so cppcheck "sees" the define.
+ */
+#ifndef CHECK
+#define CHECK(op) \
+ do { result = (op); \
+ if (result != ISC_R_SUCCESS) goto cleanup; \
+ } while (0)
+#endif
+
+#define RRTYPE_WKS_ATTRIBUTES (0)
+
+static isc_mutex_t wks_lock;
+
+static void init_lock(void) {
+ RUNTIME_CHECK(isc_mutex_init(&wks_lock) == ISC_R_SUCCESS);
+}
+
+static bool
+mygetprotobyname(const char *name, long *proto) {
+ struct protoent *pe;
+
+ LOCK(&wks_lock);
+ pe = getprotobyname(name);
+ if (pe != NULL)
+ *proto = pe->p_proto;
+ UNLOCK(&wks_lock);
+ return (pe != NULL);
+}
+
+static bool
+mygetservbyname(const char *name, const char *proto, long *port) {
+ struct servent *se;
+
+ LOCK(&wks_lock);
+ se = getservbyname(name, proto);
+ if (se != NULL)
+ *port = ntohs(se->s_port);
+ UNLOCK(&wks_lock);
+ return (se != NULL);
+}
+
+#ifdef _WIN32
+#include <windows.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#endif
+
+static inline isc_result_t
+fromtext_in_wks(ARGS_FROMTEXT) {
+ static isc_once_t once = ISC_ONCE_INIT;
+ isc_token_t token;
+ isc_region_t region;
+ struct in_addr addr;
+ 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;
+ isc_result_t result;
+
+ REQUIRE(type == dns_rdatatype_wks);
+ REQUIRE(rdclass == dns_rdataclass_in);
+
+ UNUSED(type);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(rdclass);
+
+ RUNTIME_CHECK(isc_once_do(&once, init_lock) == ISC_R_SUCCESS);
+
+#ifdef _WIN32
+ {
+ WORD wVersionRequested;
+ WSADATA wsaData;
+ int err;
+
+ wVersionRequested = MAKEWORD(2, 0);
+
+ err = WSAStartup(wVersionRequested, &wsaData );
+ if (err != 0)
+ return (ISC_R_FAILURE);
+ }
+#endif
+
+ /*
+ * IPv4 dotted quad.
+ */
+ CHECK(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ false));
+
+ isc_buffer_availableregion(target, &region);
+ if (getquad(DNS_AS_STR(token), &addr, lexer, callbacks) != 1)
+ CHECKTOK(DNS_R_BADDOTTEDQUAD);
+ if (region.length < 4)
+ return (ISC_R_NOSPACE);
+ memmove(region.base, &addr, 4);
+ isc_buffer_add(target, 4);
+
+ /*
+ * Protocol.
+ */
+ CHECK(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ false));
+
+ proto = strtol(DNS_AS_STR(token), &e, 10);
+ if (*e == 0)
+ ;
+ else if (!mygetprotobyname(DNS_AS_STR(token), &proto))
+ CHECKTOK(DNS_R_UNKNOWNPROTO);
+
+ if (proto < 0 || proto > 0xff)
+ CHECKTOK(ISC_R_RANGE);
+
+ if (proto == IPPROTO_TCP)
+ ps = "tcp";
+ else if (proto == IPPROTO_UDP)
+ ps = "udp";
+
+ CHECK(uint8_tobuffer(proto, target));
+
+ memset(bm, 0, sizeof(bm));
+ do {
+ CHECK(isc_lex_getmastertoken(lexer, &token,
+ isc_tokentype_string, 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 (!mygetservbyname(service, ps, &port) &&
+ !mygetservbyname(DNS_AS_STR(token), ps, &port))
+ CHECKTOK(DNS_R_UNKNOWNSERVICE);
+ if (port < 0 || port > 0xffff)
+ CHECKTOK(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;
+ result = mem_tobuffer(target, bm, n);
+
+ cleanup:
+#ifdef _WIN32
+ WSACleanup();
+#endif
+
+ return (result);
+}
+
+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 == dns_rdatatype_wks);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+ 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);
+ snprintf(buf, sizeof(buf), "%u", proto);
+ RETERR(str_totext(" ", target));
+ RETERR(str_totext(buf, target));
+ isc_region_consume(&sr, 1);
+
+ INSIST(sr.length <= 8*1024);
+ 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) {
+ snprintf(buf, sizeof(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 == dns_rdatatype_wks);
+ REQUIRE(rdclass == dns_rdataclass_in);
+
+ 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);
+
+ memmove(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 == dns_rdatatype_wks);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+ 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 == dns_rdatatype_wks);
+ REQUIRE(rdata1->rdclass == dns_rdataclass_in);
+ 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;
+ uint32_t a;
+
+ REQUIRE(type == dns_rdatatype_wks);
+ REQUIRE(rdclass == dns_rdataclass_in);
+ REQUIRE(source != NULL);
+ REQUIRE(wks->common.rdtype == type);
+ REQUIRE(wks->common.rdclass == rdclass);
+ REQUIRE((wks->map != NULL && wks->map_len <= 8*1024) ||
+ wks->map_len == 0);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ a = ntohl(wks->in_addr.s_addr);
+ RETERR(uint32_tobuffer(a, target));
+ RETERR(uint8_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;
+ uint32_t n;
+ isc_region_t region;
+
+ REQUIRE(rdata->type == dns_rdatatype_wks);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+ 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 = uint8_fromregion(&region);
+ isc_region_consume(&region, 1);
+ 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 == dns_rdatatype_wks);
+ REQUIRE(wks->common.rdclass == dns_rdataclass_in);
+
+ 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 == dns_rdatatype_wks);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_in_wks(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == dns_rdatatype_wks);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline bool
+checkowner_in_wks(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == dns_rdatatype_wks);
+ REQUIRE(rdclass == dns_rdataclass_in);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ return (dns_name_ishostname(name, wildcard));
+}
+
+static inline bool
+checknames_in_wks(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == dns_rdatatype_wks);
+ REQUIRE(rdata->rdclass == dns_rdataclass_in);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (true);
+}
+
+static inline int
+casecompare_in_wks(ARGS_COMPARE) {
+ return (compare_in_wks(rdata1, rdata2));
+}
+
+#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..d7883dd
--- /dev/null
+++ b/lib/dns/rdata/in_1/wks_11.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef IN_1_WKS_11_H
+#define IN_1_WKS_11_H 1
+
+
+typedef struct dns_rdata_in_wks {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ struct in_addr in_addr;
+ uint16_t protocol;
+ unsigned char *map;
+ 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..d09cb27
--- /dev/null
+++ b/lib/dns/rdata/rdatastructpre.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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..d3a8e46
--- /dev/null
+++ b/lib/dns/rdata/rdatastructsuf.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+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..59b3a74
--- /dev/null
+++ b/lib/dns/rdatalist.c
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <stddef.h>
+#include <string.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,
+ NULL,
+ NULL,
+ NULL,
+ isc__rdatalist_setownercase,
+ isc__rdatalist_getownercase
+};
+
+void
+dns_rdatalist_init(dns_rdatalist_t *rdatalist) {
+
+ REQUIRE(rdatalist != NULL);
+
+ /*
+ * Initialize rdatalist.
+ */
+
+ rdatalist->rdclass = 0;
+ rdatalist->type = 0;
+ rdatalist->covers = 0;
+ rdatalist->ttl = 0;
+ ISC_LIST_INIT(rdatalist->rdata);
+ ISC_LINK_INIT(rdatalist, link);
+ memset(rdatalist->upper, 0xeb, sizeof(rdatalist->upper));
+ /*
+ * Clear upper set bit.
+ */
+ rdatalist->upper[0] &= ~0x01;
+}
+
+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));
+
+ /* Check if dns_rdatalist_init has was called. */
+ REQUIRE(rdatalist->upper[0] == 0xea);
+
+ 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;
+
+ REQUIRE(rdataset != NULL);
+
+ 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;
+
+ REQUIRE(rdataset != NULL);
+
+ 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) {
+
+ REQUIRE(source != NULL);
+ REQUIRE(target != NULL);
+
+ *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;
+
+ REQUIRE(rdataset != NULL);
+
+ 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;
+
+ REQUIRE(rdataset != NULL);
+
+ 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 != NULL);
+ 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;
+
+ REQUIRE(rdataset != NULL);
+
+ 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 != NULL);
+ 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);
+}
+
+void
+isc__rdatalist_setownercase(dns_rdataset_t *rdataset, const dns_name_t *name) {
+ dns_rdatalist_t *rdatalist;
+ unsigned int i;
+
+ /*
+ * We do not need to worry about label lengths as they are all
+ * less than or equal to 63.
+ */
+ rdatalist = rdataset->private1;
+ memset(rdatalist->upper, 0, sizeof(rdatalist->upper));
+ for (i = 1; i < name->length; i++)
+ if (name->ndata[i] >= 0x41 && name->ndata[i] <= 0x5a)
+ rdatalist->upper[i/8] |= 1 << (i%8);
+ /*
+ * Record that upper has been set.
+ */
+ rdatalist->upper[0] |= 0x01;
+}
+
+void
+isc__rdatalist_getownercase(const dns_rdataset_t *rdataset, dns_name_t *name) {
+ dns_rdatalist_t *rdatalist;
+ unsigned int i;
+
+ rdatalist = rdataset->private1;
+ if ((rdatalist->upper[0] & 0x01) == 0)
+ return;
+ for (i = 0; i < name->length; i++) {
+ /*
+ * Set the case bit if it does not match the recorded bit.
+ */
+ if (name->ndata[i] >= 0x61 && name->ndata[i] <= 0x7a &&
+ (rdatalist->upper[i/8] & (1 << (i%8))) != 0)
+ name->ndata[i] &= ~0x20; /* clear the lower case bit */
+ else if (name->ndata[i] >= 0x41 && name->ndata[i] <= 0x5a &&
+ (rdatalist->upper[i/8] & (1 << (i%8))) == 0)
+ name->ndata[i] |= 0x20; /* set the lower case bit */
+ }
+}
diff --git a/lib/dns/rdatalist_p.h b/lib/dns/rdatalist_p.h
new file mode 100644
index 0000000..5a599fe
--- /dev/null
+++ b/lib/dns/rdatalist_p.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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);
+
+void
+isc__rdatalist_setownercase(dns_rdataset_t *rdataset, const dns_name_t *name);
+
+void
+isc__rdatalist_getownercase(const dns_rdataset_t *rdataset, dns_name_t *name);
+
+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..a2ac36f
--- /dev/null
+++ b/lib/dns/rdataset.c
@@ -0,0 +1,834 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdlib.h>
+
+#include <isc/buffer.h>
+#include <isc/mem.h>
+#include <isc/random.h>
+#include <isc/serial.h>
+#include <isc/util.h>
+
+#include <dns/compress.h>
+#include <dns/fixedname.h>
+#include <dns/name.h>
+#include <dns/ncache.h>
+#include <dns/rdata.h>
+#include <dns/rdataset.h>
+
+static const char *trustnames[] = {
+ "none",
+ "pending-additional",
+ "pending-answer",
+ "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]);
+}
+
+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 = UINT32_MAX;
+ rdataset->private1 = NULL;
+ rdataset->private2 = NULL;
+ rdataset->private3 = NULL;
+ rdataset->privateuint4 = 0;
+ rdataset->private5 = NULL;
+ rdataset->private6 = NULL;
+ rdataset->private7 = 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 = 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 = UINT32_MAX;
+ rdataset->private1 = NULL;
+ rdataset->private2 = NULL;
+ rdataset->private3 = NULL;
+ rdataset->privateuint4 = 0;
+ rdataset->private5 = NULL;
+ rdataset->private6 = NULL;
+}
+
+bool
+dns_rdataset_isassociated(dns_rdataset_t *rdataset) {
+ /*
+ * Is 'rdataset' associated?
+ */
+
+ REQUIRE(DNS_RDATASET_VALID(rdataset));
+
+ if (rdataset->methods != NULL)
+ return (true);
+
+ return (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,
+ 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,
+ bool 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 = 0, added, choice;
+ isc_buffer_t savedbuffer, rdlen, rrbuffer;
+ unsigned int headlen;
+ bool question = false;
+ bool shuffle = false;
+ dns_rdata_t *in = NULL, in_fixed[MAX_SHUFFLE];
+ struct towire_sort *out = NULL, out_fixed[MAX_SHUFFLE];
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+
+ 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(rdataset->methods != NULL);
+ REQUIRE(countp != NULL);
+ REQUIRE((order == NULL) == (order_arg == NULL));
+ REQUIRE(cctx != NULL && cctx->mctx != NULL);
+
+ if ((rdataset->attributes & DNS_RDATASETATTR_QUESTION) != 0) {
+ question = true;
+ count = 1;
+ result = dns_rdataset_first(rdataset);
+ INSIST(result == ISC_R_NOMORE);
+ } else if ((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 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 answer?
+ */
+ if (!question && count > 1 &&
+ (!WANT_FIXED(rdataset) || order != NULL) &&
+ rdataset->type != dns_rdatatype_rrsig)
+ shuffle = true;
+
+ if (shuffle && count > MAX_SHUFFLE) {
+ in = isc_mem_get(cctx->mctx, count * sizeof(*in));
+ out = isc_mem_get(cctx->mctx, count * sizeof(*out));
+ if (in == NULL || out == NULL)
+ shuffle = false;
+ } else {
+ in = in_fixed;
+ out = out_fixed;
+ }
+
+ if (shuffle) {
+ /*
+ * First we get handles to all of the rdata.
+ */
+ i = 0;
+ do {
+ INSIST(i < count);
+ dns_rdata_init(&in[i]);
+ dns_rdataset_current(rdataset, &in[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++) {
+ out[i].key = (*order)(&in[i], order_arg);
+ out[i].rdata = &in[i];
+ }
+ } else if (WANT_RANDOM(rdataset)) {
+ /*
+ * 'Random' order.
+ */
+ for (i = 0; i < count; i++) {
+ uint32_t val;
+
+ isc_random_get(&val);
+ choice = i + (val % (count - i));
+ rdata = in[i];
+ in[i] = in[choice];
+ in[choice] = rdata;
+ if (order != NULL)
+ out[i].key = (*order)(&in[i],
+ order_arg);
+ else
+ out[i].key = 0; /* Unused */
+ out[i].rdata = &in[i];
+ }
+ } else {
+ /*
+ * "Cyclic" order.
+ */
+ uint32_t val;
+ unsigned int j;
+
+ val = rdataset->count;
+ if (val == UINT32_MAX)
+ isc_random_get(&val);
+ j = val % count;
+ for (i = 0; i < count; i++) {
+ if (order != NULL)
+ out[i].key = (*order)(&in[j],
+ order_arg);
+ else
+ out[i].key = 0; /* Unused */
+ out[i].rdata = &in[j];
+ j++;
+ if (j == count)
+ j = 0; /* Wrap around. */
+ }
+ }
+
+ /*
+ * Sorted order.
+ */
+ if (order != NULL)
+ qsort(out, count, sizeof(out[0]), towire_compare);
+ }
+
+ savedbuffer = *target;
+ i = 0;
+ added = 0;
+
+ name = dns_fixedname_initname(&fixed);
+ dns_name_copy(owner_name, name, NULL);
+ dns_rdataset_getownercase(rdataset, name);
+
+ name->attributes |= owner_name->attributes &
+ DNS_NAMEATTR_NOCOMPRESS;
+
+ do {
+ /*
+ * Copy out the name, type, class, ttl.
+ */
+
+ rrbuffer = *target;
+ dns_compress_setmethods(cctx, DNS_COMPRESS_GLOBAL14);
+ result = dns_name_towire(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 = *(out[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,
+ (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, (uint16_t)rrbuffer.used);
+ *countp += added;
+ *target = rrbuffer;
+ goto cleanup;
+ }
+ INSIST(savedbuffer.used < 65536);
+ dns_compress_rollback(cctx, (uint16_t)savedbuffer.used);
+ *countp = 0;
+ *target = savedbuffer;
+
+ cleanup:
+ if (out != NULL && out != out_fixed)
+ isc_mem_put(cctx->mctx, out, count * sizeof(*out));
+ if (in != NULL && in != in_fixed)
+ isc_mem_put(cctx->mctx, in, count * sizeof(*in));
+ 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, 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, 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, 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);
+}
+
+void
+dns_rdataset_settrust(dns_rdataset_t *rdataset, dns_trust_t trust) {
+ REQUIRE(DNS_RDATASET_VALID(rdataset));
+ REQUIRE(rdataset->methods != NULL);
+
+ if (rdataset->methods->settrust != NULL)
+ (rdataset->methods->settrust)(rdataset, trust);
+ else
+ rdataset->trust = trust;
+}
+
+void
+dns_rdataset_expire(dns_rdataset_t *rdataset) {
+ REQUIRE(DNS_RDATASET_VALID(rdataset));
+ REQUIRE(rdataset->methods != NULL);
+
+ if (rdataset->methods->expire != NULL)
+ (rdataset->methods->expire)(rdataset);
+}
+
+void
+dns_rdataset_clearprefetch(dns_rdataset_t *rdataset) {
+ REQUIRE(DNS_RDATASET_VALID(rdataset));
+ REQUIRE(rdataset->methods != NULL);
+
+ if (rdataset->methods->clearprefetch != NULL)
+ (rdataset->methods->clearprefetch)(rdataset);
+}
+
+void
+dns_rdataset_setownercase(dns_rdataset_t *rdataset, const dns_name_t *name) {
+ REQUIRE(DNS_RDATASET_VALID(rdataset));
+ REQUIRE(rdataset->methods != NULL);
+
+ if (rdataset->methods->setownercase != NULL)
+ (rdataset->methods->setownercase)(rdataset, name);
+}
+
+void
+dns_rdataset_getownercase(const dns_rdataset_t *rdataset, dns_name_t *name) {
+ REQUIRE(DNS_RDATASET_VALID(rdataset));
+ REQUIRE(rdataset->methods != NULL);
+
+ if (rdataset->methods->getownercase != NULL)
+ (rdataset->methods->getownercase)(rdataset, name);
+}
+
+void
+dns_rdataset_trimttl(dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset,
+ dns_rdata_rrsig_t *rrsig, isc_stdtime_t now,
+ bool acceptexpired)
+{
+ uint32_t ttl = 0;
+
+ REQUIRE(DNS_RDATASET_VALID(rdataset));
+ REQUIRE(DNS_RDATASET_VALID(sigrdataset));
+ REQUIRE(rrsig != NULL);
+
+ /*
+ * If we accept expired RRsets keep them for no more than 120 seconds.
+ */
+ if (acceptexpired &&
+ (isc_serial_le(rrsig->timeexpire, ((now + 120) & 0xffffffff)) ||
+ isc_serial_le(rrsig->timeexpire, now)))
+ ttl = 120;
+ else if (isc_serial_ge(rrsig->timeexpire, now))
+ ttl = rrsig->timeexpire - now;
+
+ ttl = ISC_MIN(ISC_MIN(rdataset->ttl, sigrdataset->ttl),
+ ISC_MIN(rrsig->originalttl, ttl));
+ rdataset->ttl = ttl;
+ sigrdataset->ttl = ttl;
+}
diff --git a/lib/dns/rdatasetiter.c b/lib/dns/rdatasetiter.c
new file mode 100644
index 0000000..c292ef2
--- /dev/null
+++ b/lib/dns/rdatasetiter.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \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..930a822
--- /dev/null
+++ b/lib/dns/rdataslab.c
@@ -0,0 +1,1134 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \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)
+ * meta data (1 byte for RRSIG's)
+ * 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 reconstruction
+ * 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)
+{
+ /*
+ * Use &removed as a sentinal pointer for duplicate
+ * rdata as rdata.data == NULL is valid.
+ */
+ static unsigned char removed;
+ 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;
+
+ nitems = dns_rdataset_count(rdataset);
+
+ /*
+ * If there are no rdata then we can just need to allocate a header
+ * with zero a record count.
+ */
+ if (nitems == 0) {
+ if (rdataset->type != 0)
+ return (ISC_R_FAILURE);
+ rawbuf = isc_mem_get(mctx, buflen);
+ if (rawbuf == NULL)
+ return (ISC_R_NOMEMORY);
+ region->base = rawbuf;
+ region->length = buflen;
+ rawbuf += reservelen;
+ *rawbuf++ = 0;
+ *rawbuf = 0;
+ return (ISC_R_SUCCESS);
+ }
+
+ if (nitems > 0xffff)
+ return (ISC_R_NOSPACE);
+
+ /*
+ * Remember the original number of items.
+ */
+ nalloc = nitems;
+ 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 && result != ISC_R_NOMORE)
+ 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);
+ INSIST(x[i].rdata.data != &removed);
+#if DNS_RDATASET_FIXED
+ x[i].order = i;
+#endif
+ result = dns_rdataset_next(rdataset);
+ }
+ if (i != nalloc || result != ISC_R_NOMORE) {
+ /*
+ * Somehow we iterated over fewer rdatas than
+ * dns_rdataset_count() said there were or there
+ * were more items than dns_rdataset_count said
+ * there were.
+ */
+ result = ISC_R_FAILURE;
+ goto free_rdatas;
+ }
+
+ /*
+ * Put into DNSSEC order.
+ */
+ if (nalloc > 1U)
+ 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 = &removed;
+#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;
+
+ memset(rawbuf, 0, 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 == &removed)
+ continue;
+#if DNS_RDATASET_FIXED
+ offsettable[x[i].order] = rawbuf - offsetbase;
+#endif
+ length = x[i].rdata.length;
+ if (rdataset->type == dns_rdatatype_rrsig)
+ length++;
+ INSIST(length <= 0xffff);
+ *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;
+ }
+ memmove(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,
+ 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));
+}
+
+unsigned int
+dns_rdataslab_count(unsigned char *slab, unsigned int reservelen) {
+ unsigned int count;
+ unsigned char *current;
+
+ REQUIRE(slab != NULL);
+
+ current = slab + reservelen;
+ count = *current++ * 256;
+ count += *current++;
+ return (count);
+}
+
+/*
+ * 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;
+ bool offline = false;
+
+ length = *tcurrent++ * 256;
+ length += *tcurrent++;
+
+ if (type == dns_rdatatype_rrsig) {
+ if ((*tcurrent & DNS_RDATASLAB_OFFLINE) != 0)
+ offline = 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 bool
+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 (true);
+ if (n > 0) /* In DNSSEC order. */
+ break;
+ dns_rdata_reset(&trdata);
+ }
+ return (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;
+ bool added_something = 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 = 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);
+ memmove(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) {
+ bool fromold;
+ if (oadded == ocount)
+ fromold = false;
+ else if (nadded == ncount)
+ fromold = true;
+ else
+ fromold = dns_rdata_compare(&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
+ memmove(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
+ memmove(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 += (unsigned int)(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);
+ memmove(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;
+ length = (unsigned int)(mcurrent - mrdatabegin);
+#if DNS_RDATASET_FIXED
+ offsettable[order] = tcurrent - offsetbase;
+#endif
+ memmove(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);
+}
+
+bool
+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 (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 (false);
+
+ current1 += length1;
+ current2 += length1;
+
+ count1--;
+ }
+ return (true);
+}
+
+bool
+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 (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 (false);
+ dns_rdata_reset(&rdata1);
+ dns_rdata_reset(&rdata2);
+ }
+ return (true);
+}
diff --git a/lib/dns/request.c b/lib/dns/request.c
new file mode 100644
index 0000000..a235387
--- /dev/null
+++ b/lib/dns/request.c
@@ -0,0 +1,1604 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.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 */
+ int32_t eref;
+ 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;
+ bool 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;
+ 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;
+ bool canceling; /* ctlevent outstanding */
+ isc_sockaddr_t destaddr;
+ unsigned int udpcount;
+ isc_dscp_t dscp;
+};
+
+#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 /*%< canceled 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 *sock;
+ 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(sock);
+ 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; /* implicit attach */
+ requestmgr->iref = 0;
+ ISC_LIST_INIT(requestmgr->whenshutdown);
+ ISC_LIST_INIT(requestmgr->requests);
+ requestmgr->exiting = 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 *tclone;
+ 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 {
+ tclone = NULL;
+ isc_task_attach(task, &tclone);
+ event->ev_sender = tclone;
+ 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 = 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;
+ bool need_destroy = 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 = 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;
+ bool need_destroy = 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 = 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 *sock;
+ isc_socketevent_t *sendevent;
+ isc_result_t result;
+
+ req_log(ISC_LOG_DEBUG(3), "req_send: request %p", request);
+
+ REQUIRE(VALID_REQUEST(request));
+ sock = 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.
+ */
+ sendevent = isc_socket_socketevent(request->mctx, sock,
+ ISC_SOCKEVENT_SENDDONE,
+ req_senddone, request);
+ if (sendevent == NULL)
+ return (ISC_R_NOMEMORY);
+ if (request->dscp == -1) {
+ sendevent->attributes &= ~ISC_SOCKEVENTATTR_DSCP;
+ sendevent->dscp = 0;
+ } else {
+ sendevent->attributes |= ISC_SOCKEVENTATTR_DSCP;
+ sendevent->dscp = request->dscp;
+ }
+
+ request->flags |= DNS_REQUEST_F_SENDING;
+ result = isc_socket_sendto2(sock, &r, task, address, NULL,
+ sendevent, 0);
+ 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;
+ request->dscp = -1;
+ ISC_EVENT_INIT(&request->ctlevent, sizeof(request->ctlevent), 0, NULL,
+ DNS_EVENT_REQUESTCONTROL, do_cancel, request, NULL,
+ NULL, NULL);
+ request->canceling = false;
+ request->udpcount = 0;
+
+ isc_mem_attach(mctx, &request->mctx);
+
+ request->magic = REQUEST_MAGIC;
+ *requestp = request;
+ return (ISC_R_SUCCESS);
+}
+
+
+static bool
+isblackholed(dns_dispatchmgr_t *dispatchmgr, isc_sockaddr_t *destaddr) {
+ dns_acl_t *blackhole;
+ isc_netaddr_t netaddr;
+ int match;
+ bool drop = 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 = 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(bool newtcp, bool share,
+ dns_requestmgr_t *requestmgr,
+ isc_sockaddr_t *srcaddr,
+ isc_sockaddr_t *destaddr, isc_dscp_t dscp,
+ bool *connected, dns_dispatch_t **dispatchp)
+{
+ isc_result_t result;
+ isc_socket_t *sock = NULL;
+ isc_sockaddr_t src;
+ unsigned int attrs;
+ isc_sockaddr_t bind_any;
+
+ if (!newtcp && share) {
+ result = dns_dispatch_gettcp2(requestmgr->dispatchmgr,
+ destaddr, srcaddr,
+ connected, dispatchp);
+ if (result == ISC_R_SUCCESS) {
+ char peer[ISC_SOCKADDR_FORMATSIZE];
+
+ isc_sockaddr_format(destaddr, peer, sizeof(peer));
+ req_log(ISC_LOG_DEBUG(1), "attached to %s TCP "
+ "connection to %s",
+ *connected ? "existing" : "pending", peer);
+ return (result);
+ }
+ } else if (!newtcp) {
+ result = dns_dispatch_gettcp(requestmgr->dispatchmgr, destaddr,
+ srcaddr, dispatchp);
+ if (result == ISC_R_SUCCESS) {
+ char peer[ISC_SOCKADDR_FORMATSIZE];
+
+ *connected = true;
+ isc_sockaddr_format(destaddr, peer, sizeof(peer));
+ req_log(ISC_LOG_DEBUG(1), "attached to existing TCP "
+ "connection to %s", peer);
+ return (result);
+ }
+ }
+
+ result = isc_socket_create(requestmgr->socketmgr,
+ isc_sockaddr_pf(destaddr),
+ isc_sockettype_tcp, &sock);
+ 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(sock, &bind_any, 0);
+ } else {
+ src = *srcaddr;
+ isc_sockaddr_setport(&src, 0);
+ result = isc_socket_bind(sock, &src, 0);
+ }
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+#endif
+
+ attrs = 0;
+ attrs |= DNS_DISPATCHATTR_TCP;
+ if (isc_sockaddr_pf(destaddr) == AF_INET)
+ attrs |= DNS_DISPATCHATTR_IPV4;
+ else
+ attrs |= DNS_DISPATCHATTR_IPV6;
+ attrs |= DNS_DISPATCHATTR_MAKEQUERY;
+
+ isc_socket_dscp(sock, dscp);
+ result = dns_dispatch_createtcp2(requestmgr->dispatchmgr,
+ sock, requestmgr->taskmgr,
+ srcaddr, destaddr,
+ 4096, 32768, 32768, 16411, 16433,
+ attrs, dispatchp);
+cleanup:
+ isc_socket_detach(&sock);
+ 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,
+ 32768, 32768, 16411, 16433,
+ attrs, attrmask,
+ dispatchp));
+}
+
+static isc_result_t
+get_dispatch(bool tcp, bool newtcp, bool share,
+ dns_requestmgr_t *requestmgr,
+ isc_sockaddr_t *srcaddr, isc_sockaddr_t *destaddr,
+ isc_dscp_t dscp, bool *connected,
+ dns_dispatch_t **dispatchp)
+{
+ isc_result_t result;
+
+ if (tcp)
+ result = create_tcp_dispatch(newtcp, share, requestmgr,
+ srcaddr, destaddr, dscp,
+ connected, 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, 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_createraw4(requestmgr, msgbuf, srcaddr, destaddr,
+ -1, 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_createraw4(requestmgr, msgbuf, srcaddr, destaddr,
+ -1, 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)
+{
+ return (dns_request_createraw4(requestmgr, msgbuf, srcaddr, destaddr,
+ -1, options, timeout, udptimeout,
+ udpretries, task, action, arg,
+ requestp));
+}
+
+isc_result_t
+dns_request_createraw4(dns_requestmgr_t *requestmgr, isc_buffer_t *msgbuf,
+ isc_sockaddr_t *srcaddr, isc_sockaddr_t *destaddr,
+ isc_dscp_t dscp, 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 *sock = NULL;
+ isc_result_t result;
+ isc_mem_t *mctx;
+ dns_messageid_t id;
+ bool tcp = false;
+ bool newtcp = false;
+ bool share = false;
+ isc_region_t r;
+ bool connected = false;
+ unsigned int dispopt = 0;
+
+ 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;
+ request->dscp = dscp;
+
+ /*
+ * 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 = true;
+ share = (options & DNS_REQUESTOPT_SHARE);
+
+ again:
+ result = get_dispatch(tcp, newtcp, share, requestmgr,
+ srcaddr, destaddr, dscp,
+ &connected, &request->dispatch);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ if ((options & DNS_REQUESTOPT_FIXEDID) != 0) {
+ id = (r.base[0] << 8) | r.base[1];
+ dispopt |= DNS_DISPATCHOPT_FIXEDID;
+ }
+
+ result = dns_dispatch_addresponse3(request->dispatch, dispopt,
+ destaddr, task, req_response,
+ request, &id, &request->dispentry,
+ requestmgr->socketmgr);
+ if (result != ISC_R_SUCCESS) {
+ if ((options & DNS_REQUESTOPT_FIXEDID) != 0 && !newtcp) {
+ newtcp = true;
+ connected = false;
+ dns_dispatch_detach(&request->dispatch);
+ goto again;
+ }
+ goto cleanup;
+ }
+
+ sock = req_getsocket(request);
+ INSIST(sock != 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, (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 && !connected) {
+ result = isc_socket_connect(sock, 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, connected ? NULL : 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_createvia4(requestmgr, message, NULL, address,
+ -1, 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_createvia4(requestmgr, message, srcaddr, destaddr,
+ -1, 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_createvia4(requestmgr, message, srcaddr, destaddr,
+ -1, 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)
+{
+ return (dns_request_createvia4(requestmgr, message, srcaddr, destaddr,
+ -1, options, key, timeout, udptimeout,
+ udpretries, task, action, arg,
+ requestp));
+}
+
+isc_result_t
+dns_request_createvia4(dns_requestmgr_t *requestmgr, dns_message_t *message,
+ isc_sockaddr_t *srcaddr, isc_sockaddr_t *destaddr,
+ isc_dscp_t dscp, 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 *sock = NULL;
+ isc_result_t result;
+ isc_mem_t *mctx;
+ dns_messageid_t id;
+ bool tcp;
+ bool share;
+ bool settsigkey = true;
+ bool connected = false;
+
+ REQUIRE(VALID_REQUESTMGR(requestmgr));
+ REQUIRE(message != NULL);
+ REQUIRE(destaddr != NULL);
+ REQUIRE(task != NULL);
+ REQUIRE(action != NULL);
+ REQUIRE(requestp != NULL && *requestp == NULL);
+ REQUIRE(timeout > 0);
+
+ mctx = requestmgr->mctx;
+
+ req_log(ISC_LOG_DEBUG(3), "dns_request_createvia");
+
+ if (srcaddr != NULL &&
+ isc_sockaddr_pf(srcaddr) != isc_sockaddr_pf(destaddr))
+ return (ISC_R_FAMILYMISMATCH);
+
+ 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;
+ request->dscp = dscp;
+
+ /*
+ * 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 = (options & DNS_REQUESTOPT_TCP);
+ share = (options & DNS_REQUESTOPT_SHARE);
+ result = get_dispatch(tcp, false, share,
+ requestmgr, srcaddr, destaddr,
+ dscp, &connected, &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;
+ sock = req_getsocket(request);
+ INSIST(sock != NULL);
+
+ message->id = id;
+ if (settsigkey) {
+ 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);
+ sock = NULL;
+ options |= DNS_REQUESTOPT_TCP;
+ settsigkey = 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 && !connected) {
+ result = isc_socket_connect(sock, 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, connected ? NULL : 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;
+ bool tcp = false;
+ dns_compress_t cctx;
+ bool cleanup_cctx = 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 = true;
+
+ if ((options & DNS_REQUESTOPT_CASE) != 0)
+ dns_compress_setsensitive(&cctx, 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 = false;
+
+ /*
+ * Copy rendered message to exact sized buffer.
+ */
+ isc_buffer_usedregion(buf1, &r);
+ if ((options & DNS_REQUESTOPT_TCP) != 0) {
+ tcp = 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, (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 (request->event != NULL && !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 = 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 = 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);
+}
+
+bool
+dns_request_usedtcp(dns_request_t *request) {
+ REQUIRE(VALID_REQUEST(request));
+
+ return (request->flags & DNS_REQUEST_F_TCP);
+}
+
+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 *sock;
+
+ dispattr = dns_dispatch_getattributes(request->dispatch);
+ if ((dispattr & DNS_DISPATCHATTR_EXCLUSIVE) != 0) {
+ INSIST(request->dispentry != NULL);
+ sock = dns_dispatch_getentrysocket(request->dispentry);
+ } else
+ sock = dns_dispatch_getsocket(request->dispatch);
+
+ return (sock);
+}
+
+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 *sock;
+ 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);
+ sock = NULL;
+ if (DNS_REQUEST_CONNECTING(request) || DNS_REQUEST_SENDING(request)) {
+ if ((dispattr & DNS_DISPATCHATTR_EXCLUSIVE) != 0) {
+ if (request->dispentry != NULL) {
+ sock = dns_dispatch_getentrysocket(
+ request->dispentry);
+ }
+ } else
+ sock = dns_dispatch_getsocket(request->dispatch);
+ if (DNS_REQUEST_CONNECTING(request) && sock != NULL)
+ isc_socket_cancel(sock, NULL, ISC_SOCKCANCEL_CONNECT);
+ if (DNS_REQUEST_SENDING(request) && sock != NULL)
+ isc_socket_cancel(sock, 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..0abf4de
--- /dev/null
+++ b/lib/dns/resolver.c
@@ -0,0 +1,10327 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/counter.h>
+#include <isc/log.h>
+#include <isc/platform.h>
+#include <isc/print.h>
+#include <isc/string.h>
+#include <isc/random.h>
+#include <isc/socket.h>
+#include <isc/stats.h>
+#include <isc/task.h>
+#include <isc/timer.h>
+#include <isc/util.h>
+
+#ifdef AES_CC
+#include <isc/aes.h>
+#else
+#include <isc/hmacsha.h>
+#endif
+
+#include <dns/acl.h>
+#include <dns/adb.h>
+#include <dns/badcache.h>
+#include <dns/cache.h>
+#include <dns/db.h>
+#include <dns/dispatch.h>
+#include <dns/dnstap.h>
+#include <dns/ds.h>
+#include <dns/edns.h>
+#include <dns/events.h>
+#include <dns/fixedname.h>
+#include <dns/forward.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/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>
+
+#ifdef WANT_QUERYTRACE
+#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 FCTXTRACE3(m, res) \
+ isc_log_write(dns_lctx, \
+ DNS_LOGCATEGORY_RESOLVER, \
+ DNS_LOGMODULE_RESOLVER, \
+ ISC_LOG_DEBUG(3), \
+ "fctx %p(%s): [result: %s] %s", \
+ fctx, fctx->info, \
+ isc_result_totext(res), (m))
+#define FCTXTRACE4(m1, m2, res) \
+ isc_log_write(dns_lctx, \
+ DNS_LOGCATEGORY_RESOLVER, \
+ DNS_LOGMODULE_RESOLVER, \
+ ISC_LOG_DEBUG(3), \
+ "fctx %p(%s): [result: %s] %s %s", \
+ fctx, fctx->info, \
+ isc_result_totext(res), (m1), (m2))
+#define FCTXTRACE5(m1, m2, v) \
+ isc_log_write(dns_lctx, \
+ DNS_LOGCATEGORY_RESOLVER, \
+ DNS_LOGMODULE_RESOLVER, \
+ ISC_LOG_DEBUG(3), \
+ "fctx %p(%s): %s %s%u", \
+ fctx, fctx->info, (m1), (m2), (v))
+#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) do { UNUSED(m); } while (0)
+#define RRTRACE(r, m) do { UNUSED(r); UNUSED(m); } while (0)
+#define FCTXTRACE(m) do { UNUSED(m); } while (0)
+#define FCTXTRACE2(m1, m2) do { UNUSED(m1); UNUSED(m2); } while (0)
+#define FCTXTRACE3(m1, res) do { UNUSED(m1); UNUSED(res); } while (0)
+#define FCTXTRACE4(m1, m2, res) \
+ do { UNUSED(m1); UNUSED(m2); UNUSED(res); } while (0)
+#define FCTXTRACE5(m1, m2, v) \
+ do { UNUSED(m1); UNUSED(m2); UNUSED(v); } while (0)
+#define FTRACE(m) do { UNUSED(m); } while (0)
+#define QTRACE(m) do { UNUSED(m); } while (0)
+#endif /* WANT_QUERYTRACE */
+
+#define US_PER_SEC 1000000U
+/*
+ * The maximum time we will wait for a single query.
+ */
+#define MAX_SINGLE_QUERY_TIMEOUT 9U
+#define MAX_SINGLE_QUERY_TIMEOUT_US (MAX_SINGLE_QUERY_TIMEOUT*US_PER_SEC)
+
+/*
+ * We need to allow a individual query time to complete / timeout.
+ */
+#define MINIMUM_QUERY_TIMEOUT (MAX_SINGLE_QUERY_TIMEOUT + 1U)
+
+/* The default time in seconds for the whole query to live. */
+#ifndef DEFAULT_QUERY_TIMEOUT
+#define DEFAULT_QUERY_TIMEOUT MINIMUM_QUERY_TIMEOUT
+#endif
+
+/* The maximum time in seconds for the whole query to live. */
+#ifndef MAXIMUM_QUERY_TIMEOUT
+#define MAXIMUM_QUERY_TIMEOUT 30
+#endif
+
+/* The default maximum number of recursions to follow before giving up. */
+#ifndef DEFAULT_RECURSION_DEPTH
+#define DEFAULT_RECURSION_DEPTH 7
+#endif
+
+/* The default maximum number of iterative queries to allow before giving up. */
+#ifndef DEFAULT_MAX_QUERIES
+#define DEFAULT_MAX_QUERIES 75
+#endif
+
+/* Number of hash buckets for zone counters */
+#ifndef RES_DOMAIN_BUCKETS
+#define RES_DOMAIN_BUCKETS 523
+#endif
+#define RES_NOBUCKET 0xffffffff
+
+/*%
+ * 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
+
+#define DNS_RESOLVER_BADCACHESIZE 1021
+#define DNS_RESOLVER_BADCACHETTL(fctx) \
+ (((fctx)->res->lame_ttl > 30 ) ? (fctx)->res->lame_ttl : 30)
+
+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;
+ bool 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;
+ isc_socketevent_t sendevent;
+ isc_dscp_t dscp;
+ int ednsversion;
+ unsigned int options;
+ unsigned int attributes;
+ unsigned int sends;
+ unsigned int connects;
+ unsigned int udpsize;
+ unsigned char data[512];
+} resquery_t;
+
+struct tried {
+ isc_sockaddr_t addr;
+ unsigned int count;
+ ISC_LINK(struct tried) link;
+};
+
+#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;
+
+typedef enum {
+ badns_unreachable = 0,
+ badns_response,
+ badns_validation
+} badnstype_t;
+
+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;
+ unsigned int dbucketnum;
+ char * info;
+ isc_mem_t * mctx;
+
+ /*% Locked by appropriate bucket lock. */
+ fetchstate state;
+ bool want_shutdown;
+ bool cloned;
+ bool 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;
+ dns_forwarderlist_t forwarders;
+ dns_fwdpolicy_t fwdpolicy;
+ isc_sockaddrlist_t bad;
+ ISC_LIST(struct tried) edns;
+ ISC_LIST(struct tried) edns512;
+ isc_sockaddrlist_t bad_edns;
+ dns_validator_t * validator;
+ ISC_LIST(dns_validator_t) validators;
+ dns_db_t * cache;
+ dns_adb_t * adb;
+ bool ns_ttl_ok;
+ uint32_t ns_ttl;
+ isc_counter_t * qc;
+
+ /*%
+ * 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.
+ */
+ uint32_t rand_buf;
+ uint32_t rand_bits;
+
+ /*%
+ * Fetch-local statistics for detailed logging.
+ */
+ isc_result_t result; /*%< fetch result */
+ isc_result_t vresult; /*%< validation result */
+ int exitline;
+ isc_time_t start;
+ uint64_t duration;
+ bool logged;
+ unsigned int querysent;
+ unsigned int referrals;
+ unsigned int lamecount;
+ unsigned int quotacount;
+ unsigned int neterr;
+ unsigned int badresp;
+ unsigned int adberr;
+ unsigned int findfail;
+ unsigned int valfail;
+ bool timeout;
+ dns_adbaddrinfo_t *addrinfo;
+ isc_sockaddr_t *client;
+ unsigned int depth;
+};
+
+#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;
+ isc_mem_t * mctx;
+ 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;
+ bool exiting;
+ isc_mem_t * mctx;
+} fctxbucket_t;
+
+typedef struct fctxcount fctxcount_t;
+struct fctxcount {
+ dns_fixedname_t fdname;
+ dns_name_t *domain;
+ uint32_t count;
+ uint32_t allowed;
+ uint32_t dropped;
+ isc_stdtime_t logged;
+ ISC_LINK(fctxcount_t) link;
+};
+
+typedef struct zonebucket {
+ isc_mutex_t lock;
+ isc_mem_t *mctx;
+ ISC_LIST(fctxcount_t) list;
+} zonebucket_t;
+
+typedef struct alternate {
+ bool 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;
+ bool frozen;
+ unsigned int options;
+ dns_dispatchmgr_t * dispatchmgr;
+ dns_dispatchset_t * dispatches4;
+ bool exclusivev4;
+ dns_dispatchset_t * dispatches6;
+ isc_dscp_t querydscp4;
+ isc_dscp_t querydscp6;
+ bool exclusivev6;
+ unsigned int nbuckets;
+ fctxbucket_t * buckets;
+ zonebucket_t * dbuckets;
+ uint32_t lame_ttl;
+ ISC_LIST(alternate_t) alternates;
+ uint16_t udpsize;
+#if USE_ALGLOCK
+ isc_rwlock_t alglock;
+#endif
+ dns_rbt_t * algorithms;
+ dns_rbt_t * digests;
+#if USE_MBSLOCK
+ isc_rwlock_t mbslock;
+#endif
+ dns_rbt_t * mustbesecure;
+ unsigned int spillatmax;
+ unsigned int spillatmin;
+ isc_timer_t * spillattimer;
+ bool zero_no_soa_ttl;
+ unsigned int query_timeout;
+ unsigned int maxdepth;
+ unsigned int maxqueries;
+ isc_result_t quotaresp[2];
+
+ /* Locked by lock. */
+ unsigned int references;
+ bool exiting;
+ isc_eventlist_t whenshutdown;
+ unsigned int activebuckets;
+ bool priming;
+ unsigned int spillat; /* clients-per-query */
+ unsigned int zspill; /* fetches-per-zone */
+
+ dns_badcache_t * badcache; /* Bad cache. */
+
+ /* 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
+ * (0x008) which we also use as an addrinfo flag.
+ */
+#define FCTX_ADDRINFO_MARK 0x00001
+#define FCTX_ADDRINFO_FORWARDER 0x01000
+#define FCTX_ADDRINFO_EDNSOK 0x04000
+#define FCTX_ADDRINFO_NOCOOKIE 0x08000
+#define FCTX_ADDRINFO_BADCOOKIE 0x10000
+
+#define UNMARKED(a) (((a)->flags & FCTX_ADDRINFO_MARK) \
+ == 0)
+#define ISFORWARDER(a) (((a)->flags & \
+ FCTX_ADDRINFO_FORWARDER) != 0)
+#define NOCOOKIE(a) (((a)->flags & \
+ FCTX_ADDRINFO_NOCOOKIE) != 0)
+#define EDNSOK(a) (((a)->flags & \
+ FCTX_ADDRINFO_EDNSOK) != 0)
+#define BADCOOKIE(a) (((a)->flags & \
+ FCTX_ADDRINFO_BADCOOKIE) != 0)
+
+
+#define NXDOMAIN(r) (((r)->attributes & DNS_RDATASETATTR_NXDOMAIN) != 0)
+#define NEGATIVE(r) (((r)->attributes & DNS_RDATASETATTR_NEGATIVE) != 0)
+
+#ifdef ENABLE_AFL
+bool dns_fuzzing_resolver = false;
+void dns_resolver_setfuzzing() {
+ dns_fuzzing_resolver = true;
+}
+#endif
+
+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, bool retrying,
+ bool badcache);
+static void fctx_destroy(fetchctx_t *fctx);
+static bool fctx_unlink(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,
+ bool optout,
+ bool secure,
+ dns_rdataset_t *ardataset,
+ isc_result_t *eresultp);
+static void validated(isc_task_t *task, isc_event_t *event);
+static bool maybe_destroy(fetchctx_t *fctx, bool locked);
+static void add_bad(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
+ isc_result_t reason, badnstype_t badtype);
+static inline isc_result_t findnoqname(fetchctx_t *fctx, dns_name_t *name,
+ dns_rdatatype_t type,
+ dns_name_t **noqname);
+static void fctx_increference(fetchctx_t *fctx);
+static bool fctx_decreference(fetchctx_t *fctx);
+
+/*%
+ * Increment resolver-related statistics counters.
+ */
+static inline void
+inc_stats(dns_resolver_t *res, isc_statscounter_t counter) {
+ if (res->view->resstats != NULL)
+ isc_stats_increment(res->view->resstats, counter);
+}
+
+static inline void
+dec_stats(dns_resolver_t *res, isc_statscounter_t counter) {
+ if (res->view->resstats != NULL)
+ isc_stats_decrement(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->mctx, sizeof(*valarg));
+ if (valarg == NULL)
+ return (ISC_R_NOMEMORY);
+
+ valarg->fctx = fctx;
+ valarg->addrinfo = addrinfo;
+
+ if (!ISC_LIST_EMPTY(fctx->validators))
+ valoptions |= DNS_VALIDATOR_DEFER;
+ else
+ valoptions &= ~DNS_VALIDATOR_DEFER;
+
+ 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->mctx, valarg, sizeof(*valarg));
+ return (result);
+}
+
+static bool
+rrsig_fromchildzone(fetchctx_t *fctx, dns_rdataset_t *rdataset) {
+ dns_namereln_t namereln;
+ dns_rdata_rrsig_t rrsig;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ int order;
+ isc_result_t result;
+ unsigned int labels;
+
+ 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);
+ namereln = dns_name_fullcompare(&rrsig.signer, &fctx->domain,
+ &order, &labels);
+ if (namereln == dns_namereln_subdomain)
+ return (true);
+ dns_rdata_reset(&rdata);
+ }
+ return (false);
+}
+
+static bool
+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;
+ bool keep_auth = false;
+
+ if (message->rcode == dns_rcode_nxdomain)
+ return (false);
+
+ /*
+ * A DS RRset can appear anywhere in a zone, even for a delegation-only
+ * zone. So a response to an explicit query for this type should be
+ * excluded from delegation-only fixup.
+ *
+ * SOA, NS, and DNSKEY can only exist at a zone apex, so a postive
+ * response to a query for these types can never violate the
+ * delegation-only assumption: if the query name is below a
+ * zone cut, the response should normally be a referral, which should
+ * be accepted; if the query name is below a zone cut but the server
+ * happens to have authority for the zone of the query name, the
+ * response is a (non-referral) answer. But this does not violate
+ * delegation-only because the query name must be in a different zone
+ * due to the "apex-only" nature of these types. Note that if the
+ * remote server happens to have authority for a child zone of a
+ * delegation-only zone, we may still incorrectly "fix" the response
+ * with NXDOMAIN for queries for other types. Unfortunately it's
+ * generally impossible to differentiate this case from violation of
+ * the delegation-only assumption. Once the resolver learns the
+ * correct zone cut, possibly via a separate query for an "apex-only"
+ * type, queries for other types will be resolved correctly.
+ *
+ * A query for type ANY will be accepted if it hits an exceptional
+ * type above in the answer section as it should be from a child
+ * zone.
+ *
+ * Also accept answers with RRSIG records from the child zone.
+ * Direct queries for RRSIG records should not be answered from
+ * the parent zone.
+ */
+
+ if (message->counts[DNS_SECTION_ANSWER] != 0 &&
+ (fctx->type == dns_rdatatype_ns ||
+ fctx->type == dns_rdatatype_ds ||
+ fctx->type == dns_rdatatype_soa ||
+ fctx->type == dns_rdatatype_any ||
+ fctx->type == dns_rdatatype_rrsig ||
+ fctx->type == dns_rdatatype_dnskey)) {
+ 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)) {
+ if (!dns_name_equal(name, &fctx->name))
+ continue;
+ type = rdataset->type;
+ /*
+ * RRsig from child?
+ */
+ if (type == dns_rdatatype_rrsig &&
+ rrsig_fromchildzone(fctx, rdataset))
+ return (false);
+ /*
+ * Direct query for apex records or DS.
+ */
+ if (fctx->type == type &&
+ (type == dns_rdatatype_ds ||
+ type == dns_rdatatype_ns ||
+ type == dns_rdatatype_soa ||
+ type == dns_rdatatype_dnskey))
+ return (false);
+ /*
+ * Indirect query for apex records or DS.
+ */
+ if (fctx->type == dns_rdatatype_any &&
+ (type == dns_rdatatype_ns ||
+ type == dns_rdatatype_ds ||
+ type == dns_rdatatype_soa ||
+ type == dns_rdatatype_dnskey))
+ return (false);
+ }
+ result = dns_message_nextname(message,
+ DNS_SECTION_ANSWER);
+ }
+ }
+
+ /*
+ * A NODATA response to a DS query?
+ */
+ if (fctx->type == dns_rdatatype_ds &&
+ message->counts[DNS_SECTION_ANSWER] == 0)
+ return (false);
+
+ /* Look for referral or indication of answer from child zone? */
+ 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 = true;
+
+ if (type != dns_rdatatype_ns &&
+ type != dns_rdatatype_soa &&
+ type != dns_rdatatype_rrsig)
+ continue;
+
+ if (type == dns_rdatatype_rrsig) {
+ if (rrsig_fromchildzone(fctx, rdataset))
+ return (false);
+ else
+ continue;
+ }
+
+ /* NS or SOA records. */
+ if (dns_name_equal(name, domain)) {
+ /*
+ * If a query for ANY causes a negative
+ * response, we can be sure that this is
+ * an empty node. For other type of queries
+ * we cannot differentiate an empty node
+ * from a node that just doesn't have that
+ * type of record. We only accept the former
+ * case.
+ */
+ if (message->counts[DNS_SECTION_ANSWER] == 0 &&
+ fctx->type == dns_rdatatype_any)
+ return (false);
+ } else if (dns_name_issubdomain(name, domain)) {
+ /* Referral or answer from child zone. */
+ return (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 (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, 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, 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, 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) {
+ dns_resolver_t *res;
+ bool empty;
+ resquery_t *query;
+ fetchctx_t *fctx;
+ unsigned int bucket;
+
+ REQUIRE(queryp != NULL);
+ query = *queryp;
+ REQUIRE(!ISC_LINK_LINKED(query, link));
+
+ INSIST(query->tcpsocket == NULL);
+
+ fctx = query->fctx;
+ res = fctx->res;
+ bucket = fctx->bucketnum;
+
+ fctx->nqueries--;
+
+ LOCK(&res->buckets[bucket].lock);
+ empty = fctx_decreference(query->fctx);
+ UNLOCK(&res->buckets[bucket].lock);
+
+ query->magic = 0;
+ isc_mem_put(query->mctx, query, sizeof(*query));
+ *queryp = NULL;
+
+ if (empty)
+ empty_bucket(res);
+}
+
+static void
+fctx_cancelquery(resquery_t **queryp, dns_dispatchevent_t **deventp,
+ isc_time_t *finish, bool no_response,
+ bool age_untried)
+{
+ fetchctx_t *fctx;
+ resquery_t *query;
+ unsigned int rtt, rttms;
+ unsigned int factor;
+ dns_adbfind_t *find;
+ dns_adbaddrinfo_t *addrinfo;
+ isc_socket_t *sock;
+ isc_stdtime_t now;
+
+ 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;
+
+ rttms = rtt / 1000;
+ if (rttms < DNS_RESOLVER_QRYRTTCLASS0) {
+ inc_stats(fctx->res,
+ dns_resstatscounter_queryrtt0);
+ } else if (rttms < DNS_RESOLVER_QRYRTTCLASS1) {
+ inc_stats(fctx->res,
+ dns_resstatscounter_queryrtt1);
+ } else if (rttms < DNS_RESOLVER_QRYRTTCLASS2) {
+ inc_stats(fctx->res,
+ dns_resstatscounter_queryrtt2);
+ } else if (rttms < DNS_RESOLVER_QRYRTTCLASS3) {
+ inc_stats(fctx->res,
+ dns_resstatscounter_queryrtt3);
+ } else if (rttms < DNS_RESOLVER_QRYRTTCLASS4) {
+ inc_stats(fctx->res,
+ dns_resstatscounter_queryrtt4);
+ } else {
+ inc_stats(fctx->res,
+ dns_resstatscounter_queryrtt5);
+ }
+ } else {
+ uint32_t value;
+ uint32_t mask;
+
+ if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0)
+ dns_adb_ednsto(fctx->adb, query->addrinfo,
+ query->udpsize);
+ else
+ dns_adb_timeout(fctx->adb, query->addrinfo);
+
+ /*
+ * 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);
+ isc_random_get(&value);
+ if (query->addrinfo->srtt > 800000)
+ mask = 0x3fff;
+ else if (query->addrinfo->srtt > 400000)
+ mask = 0x7fff;
+ else if (query->addrinfo->srtt > 200000)
+ mask = 0xffff;
+ else if (query->addrinfo->srtt > 100000)
+ mask = 0x1ffff;
+ else if (query->addrinfo->srtt > 50000)
+ mask = 0x3ffff;
+ else if (query->addrinfo->srtt > 25000)
+ mask = 0x7ffff;
+ else
+ mask = 0xfffff;
+
+ /*
+ * Don't adjust timeout on EDNS queries unless we have
+ * seen a EDNS response.
+ */
+ if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0 &&
+ !EDNSOK(query->addrinfo)) {
+ mask >>= 2;
+ }
+
+ rtt = query->addrinfo->srtt + (value & mask);
+ if (rtt > MAX_SINGLE_QUERY_TIMEOUT_US)
+ rtt = MAX_SINGLE_QUERY_TIMEOUT_US;
+
+ /*
+ * Replace the current RTT with our value.
+ */
+ factor = DNS_ADB_RTTADJREPLACE;
+ }
+
+ dns_adb_adjustsrtt(fctx->adb, query->addrinfo, rtt, factor);
+ }
+ dns_adb_endudpfetch(fctx->adb, query->addrinfo);
+
+ /*
+ * Age RTTs of servers not tried.
+ */
+ isc_stdtime_get(&now);
+ if (finish != NULL || age_untried)
+ for (addrinfo = ISC_LIST_HEAD(fctx->forwaddrs);
+ addrinfo != NULL;
+ addrinfo = ISC_LIST_NEXT(addrinfo, publink))
+ if (UNMARKED(addrinfo))
+ dns_adb_agesrtt(fctx->adb, addrinfo, now);
+
+ if ((finish != NULL || age_untried) && 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_agesrtt(fctx->adb, addrinfo,
+ now);
+
+ if ((finish != NULL || age_untried) && TRIEDALT(fctx)) {
+ for (addrinfo = ISC_LIST_HEAD(fctx->altaddrs);
+ addrinfo != NULL;
+ addrinfo = ISC_LIST_NEXT(addrinfo, publink))
+ if (UNMARKED(addrinfo))
+ dns_adb_agesrtt(fctx->adb, addrinfo, now);
+ 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_agesrtt(fctx->adb, addrinfo,
+ now);
+ }
+
+ /*
+ * 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);
+ sock = dns_dispatch_getentrysocket(query->dispentry);
+ if (sock != NULL)
+ isc_socket_cancel(sock, NULL,
+ ISC_SOCKCANCEL_CONNECT);
+ }
+ } else if (RESQUERY_SENDING(query)) {
+ /*
+ * Cancel the pending send.
+ */
+ if (query->exclusivesocket && query->dispentry != NULL)
+ sock = dns_dispatch_getentrysocket(query->dispentry);
+ else
+ sock = dns_dispatch_getsocket(query->dispatch);
+ if (sock != NULL)
+ isc_socket_cancel(sock, 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, bool no_response,
+ bool age_untried)
+{
+ 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,
+ age_untried);
+ }
+}
+
+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_stopqueries(fetchctx_t *fctx, bool no_response,
+ bool age_untried)
+{
+ FCTXTRACE("stopqueries");
+ fctx_cancelqueries(fctx, no_response, age_untried);
+ fctx_stoptimer(fctx);
+}
+
+static inline void
+fctx_cleanupall(fetchctx_t *fctx) {
+ fctx_cleanupfinds(fctx);
+ fctx_cleanupaltfinds(fctx);
+ fctx_cleanupforwaddrs(fctx);
+ fctx_cleanupaltaddrs(fctx);
+}
+
+static void
+fcount_logspill(fetchctx_t *fctx, fctxcount_t *counter) {
+ char dbuf[DNS_NAME_FORMATSIZE];
+ isc_stdtime_t now;
+
+ if (! isc_log_wouldlog(dns_lctx, ISC_LOG_INFO))
+ return;
+
+ isc_stdtime_get(&now);
+ if (counter->logged > now - 60)
+ return;
+
+ dns_name_format(&fctx->domain, dbuf, sizeof(dbuf));
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_SPILL,
+ DNS_LOGMODULE_RESOLVER, ISC_LOG_INFO,
+ "too many simultaneous fetches for %s "
+ "(allowed %d spilled %d)",
+ dbuf, counter->allowed, counter->dropped);
+
+ counter->logged = now;
+}
+
+static isc_result_t
+fcount_incr(fetchctx_t *fctx, bool force) {
+ isc_result_t result = ISC_R_SUCCESS;
+ zonebucket_t *dbucket;
+ fctxcount_t *counter;
+ unsigned int bucketnum, spill;
+
+ REQUIRE(fctx != NULL);
+ REQUIRE(fctx->res != NULL);
+
+ INSIST(fctx->dbucketnum == RES_NOBUCKET);
+ bucketnum = dns_name_fullhash(&fctx->domain, false)
+ % RES_DOMAIN_BUCKETS;
+
+ LOCK(&fctx->res->lock);
+ spill = fctx->res->zspill;
+ UNLOCK(&fctx->res->lock);
+
+ dbucket = &fctx->res->dbuckets[bucketnum];
+
+ LOCK(&dbucket->lock);
+ for (counter = ISC_LIST_HEAD(dbucket->list);
+ counter != NULL;
+ counter = ISC_LIST_NEXT(counter, link))
+ {
+ if (dns_name_equal(counter->domain, &fctx->domain))
+ break;
+ }
+
+ if (counter == NULL) {
+ counter = isc_mem_get(dbucket->mctx, sizeof(fctxcount_t));
+ if (counter == NULL)
+ result = ISC_R_NOMEMORY;
+ else {
+ ISC_LINK_INIT(counter, link);
+ counter->count = 1;
+ counter->logged = 0;
+ counter->allowed = 1;
+ counter->dropped = 0;
+ counter->domain =
+ dns_fixedname_initname(&counter->fdname);
+ dns_name_copy(&fctx->domain, counter->domain, NULL);
+ ISC_LIST_APPEND(dbucket->list, counter, link);
+ }
+ } else {
+ if (!force && spill != 0 && counter->count >= spill) {
+ counter->dropped++;
+ fcount_logspill(fctx, counter);
+ result = ISC_R_QUOTA;
+ } else {
+ counter->count++;
+ counter->allowed++;
+ }
+ }
+ UNLOCK(&dbucket->lock);
+
+ if (result == ISC_R_SUCCESS)
+ fctx->dbucketnum = bucketnum;
+
+ return (result);
+}
+
+static void
+fcount_decr(fetchctx_t *fctx) {
+ zonebucket_t *dbucket;
+ fctxcount_t *counter;
+
+ REQUIRE(fctx != NULL);
+
+ if (fctx->dbucketnum == RES_NOBUCKET)
+ return;
+
+ dbucket = &fctx->res->dbuckets[fctx->dbucketnum];
+
+ LOCK(&dbucket->lock);
+ for (counter = ISC_LIST_HEAD(dbucket->list);
+ counter != NULL;
+ counter = ISC_LIST_NEXT(counter, link))
+ {
+ if (dns_name_equal(counter->domain, &fctx->domain))
+ break;
+ }
+
+ if (counter != NULL) {
+ INSIST(counter->count != 0);
+ counter->count--;
+ fctx->dbucketnum = RES_NOBUCKET;
+
+ if (counter->count == 0) {
+ ISC_LIST_UNLINK(dbucket->list, counter, link);
+ isc_mem_put(dbucket->mctx, counter, sizeof(*counter));
+ }
+ }
+
+ UNLOCK(&dbucket->lock);
+}
+
+static inline void
+fctx_sendevents(fetchctx_t *fctx, isc_result_t result, int line) {
+ dns_fetchevent_t *event, *next_event;
+ isc_task_t *task;
+ unsigned int count = 0;
+ isc_interval_t i;
+ bool logit = false;
+ isc_time_t now;
+ 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");
+
+ /*
+ * Keep some record of fetch result for logging later (if required).
+ */
+ fctx->result = result;
+ fctx->exitline = line;
+ TIME_NOW(&now);
+ fctx->duration = isc_time_microdiff(&now, &fctx->start);
+
+ 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;
+ event->vresult = fctx->vresult;
+ if (!HAVE_ANSWER(fctx))
+ event->result = result;
+
+ INSIST(event->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) &&
+ NEGATIVE(event->rdataset)) {
+ 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 = true;
+ }
+ isc_interval_set(&i, 20 * 60, 0);
+ result = isc_timer_reset(fctx->res->spillattimer,
+ isc_timertype_ticker, NULL,
+ &i, 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;
+
+ /*
+ * We do not know if fctx->domain is the actual domain the record
+ * lives in or a parent domain so we have a '?' after it.
+ */
+ 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, int line) {
+ dns_resolver_t *res;
+ bool no_response = false;
+ bool age_untried = false;
+
+ REQUIRE(line >= 0);
+
+ FCTXTRACE("done");
+
+ res = fctx->res;
+
+ if (result == ISC_R_SUCCESS) {
+ /*%
+ * Log any deferred EDNS timeout messages.
+ */
+ log_edns(fctx);
+ no_response = true;
+ } else if (result == ISC_R_TIMEDOUT)
+ age_untried = true;
+
+ fctx->reason = NULL;
+
+ fctx_stopqueries(fctx, no_response, age_untried);
+
+ LOCK(&res->buckets[fctx->bucketnum].lock);
+
+ fctx->state = fetchstate_done;
+ fctx->attributes &= ~FCTX_ATTR_ADDRWAIT;
+ fctx_sendevents(fctx, result, line);
+
+ 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;
+ bool destroy_query = false;
+ bool retry = 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);
+ destroy_query = true;
+ }
+ } 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:
+ FCTXTRACE3("query canceled in sendevent(): "
+ "no route to host; no response",
+ sevent->result);
+
+ /*
+ * No route to remote.
+ */
+ add_bad(fctx, query->addrinfo, sevent->result,
+ badns_unreachable);
+ fctx_cancelquery(&query, NULL, NULL, true,
+ false);
+ retry = true;
+ break;
+
+ default:
+ FCTXTRACE3("query canceled in sendevent() due to "
+ "unexpected event result; responding",
+ sevent->result);
+
+ fctx_cancelquery(&query, NULL, NULL, false,
+ false);
+ break;
+ }
+ }
+
+ if (event->ev_type == ISC_SOCKEVENT_CONNECT)
+ 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, __LINE__);
+ else
+ fctx_try(fctx, true, false);
+ }
+
+ if (destroy_query)
+ resquery_destroy(&query);
+}
+
+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,
+ uint16_t udpsize, dns_ednsopt_t *ednsopts, size_t count)
+{
+ dns_rdataset_t *rdataset = NULL;
+ isc_result_t result;
+
+ result = dns_message_buildopt(message, &rdataset, version, udpsize,
+ DNS_MESSAGEEXTFLAG_DO, ednsopts, count);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ 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));
+
+ /*
+ * Add a fudge factor to the expected rtt based on the current
+ * estimate.
+ */
+ if (rtt < 50000)
+ rtt += 50000;
+ else if (rtt < 100000)
+ rtt += 100000;
+ else
+ rtt += 200000;
+
+ /*
+ * Always wait for at least the expected rtt.
+ */
+ if (us < rtt)
+ us = rtt;
+
+ /*
+ * But don't ever wait for more than 10 seconds.
+ */
+ if (us > MAX_SINGLE_QUERY_TIMEOUT_US)
+ us = MAX_SINGLE_QUERY_TIMEOUT_US;
+
+ seconds = us / US_PER_SEC;
+ us -= seconds * US_PER_SEC;
+ 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;
+ bool have_addr = false;
+ unsigned int srtt;
+ isc_dscp_t dscp = -1;
+
+ FCTXTRACE("query");
+
+ res = fctx->res;
+ task = res->buckets[fctx->bucketnum].task;
+
+ srtt = addrinfo->srtt;
+
+ /*
+ * A forwarder needs to make multiple queries. Give it at least
+ * a second to do these in.
+ */
+ 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(fctx->mctx, sizeof(*query));
+ if (query == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto stop_idle_timer;
+ }
+ query->mctx = fctx->mctx;
+ query->options = options;
+ query->attributes = 0;
+ query->sends = 0;
+ query->connects = 0;
+ query->dscp = addrinfo->dscp;
+ query->udpsize = 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 = false;
+ query->tcpsocket = NULL;
+ if (res->view->peers != NULL) {
+ dns_peer_t *peer = NULL;
+ isc_netaddr_t dstip;
+ bool usetcp = false;
+ 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 = true;
+ result = dns_peer_getquerydscp(peer, &dscp);
+ if (result == ISC_R_SUCCESS)
+ query->dscp = dscp;
+ result = dns_peer_getforcetcp(peer, &usetcp);
+ if (result == ISC_R_SUCCESS && usetcp)
+ query->options |= DNS_FETCHOPT_TCP;
+ }
+ }
+
+ dscp = -1;
+ 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->dispatches4->dispatches[0],
+ &addr);
+ dscp = dns_resolver_getquerydscp4(fctx->res);
+ break;
+ case PF_INET6:
+ result = dns_dispatch_getlocaladdress(
+ res->dispatches6->dispatches[0],
+ &addr);
+ dscp = dns_resolver_getquerydscp6(fctx->res);
+ break;
+ default:
+ result = ISC_R_NOTIMPLEMENTED;
+ break;
+ }
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_query;
+ }
+ isc_sockaddr_setport(&addr, 0);
+ if (query->dscp == -1)
+ query->dscp = dscp;
+
+ 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;
+ dscp = dns_resolver_getquerydscp4(fctx->res);
+ break;
+ case AF_INET6:
+ attrs |= DNS_DISPATCHATTR_IPV6;
+ dscp = dns_resolver_getquerydscp6(fctx->res);
+ 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, 20000, 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(
+ dns_resolver_dispatchv4(res),
+ &query->dispatch);
+ query->exclusivesocket = res->exclusivev4;
+ dscp = dns_resolver_getquerydscp4(fctx->res);
+ break;
+ case PF_INET6:
+ dns_dispatch_attach(
+ dns_resolver_dispatchv6(res),
+ &query->dispatch);
+ query->exclusivesocket = res->exclusivev6;
+ dscp = dns_resolver_getquerydscp6(fctx->res);
+ break;
+ default:
+ result = ISC_R_NOTIMPLEMENTED;
+ goto cleanup_query;
+ }
+ }
+
+ if (query->dscp == -1)
+ query->dscp = dscp;
+ /*
+ * 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; /* reference added by caller */
+ 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?
+ */
+ if (query->dscp != -1)
+ isc_socket_dscp(query->tcpsocket, query->dscp);
+ 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 {
+ if (dns_adbentry_overquota(addrinfo->entry))
+ goto cleanup_dispatch;
+
+ /* Inform the ADB that we're starting a fetch */
+ dns_adb_beginudpfetch(fctx->adb, addrinfo);
+
+ result = resquery_send(query);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_dispatch;
+ }
+
+ fctx->querysent++;
+
+ 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:
+ if (query->connects == 0) {
+ query->magic = 0;
+ isc_mem_put(fctx->mctx, query, sizeof(*query));
+ }
+
+ stop_idle_timer:
+ RUNTIME_CHECK(fctx_stopidletimer(fctx) == ISC_R_SUCCESS);
+
+ return (result);
+}
+
+static bool
+bad_edns(fetchctx_t *fctx, isc_sockaddr_t *address) {
+ isc_sockaddr_t *sa;
+
+ for (sa = ISC_LIST_HEAD(fctx->bad_edns);
+ sa != NULL;
+ sa = ISC_LIST_NEXT(sa, link)) {
+ if (isc_sockaddr_equal(sa, address))
+ return (true);
+ }
+
+ return (false);
+}
+
+static void
+add_bad_edns(fetchctx_t *fctx, isc_sockaddr_t *address) {
+ isc_sockaddr_t *sa;
+
+#ifdef ENABLE_AFL
+ if (dns_fuzzing_resolver)
+ return;
+#endif
+ if (bad_edns(fctx, address))
+ return;
+
+ sa = isc_mem_get(fctx->mctx, sizeof(*sa));
+ if (sa == NULL)
+ return;
+
+ *sa = *address;
+ ISC_LIST_INITANDAPPEND(fctx->bad_edns, sa, link);
+}
+
+static struct tried *
+triededns(fetchctx_t *fctx, isc_sockaddr_t *address) {
+ struct tried *tried;
+
+ for (tried = ISC_LIST_HEAD(fctx->edns);
+ tried != NULL;
+ tried = ISC_LIST_NEXT(tried, link)) {
+ if (isc_sockaddr_equal(&tried->addr, address))
+ return (tried);
+ }
+
+ return (NULL);
+}
+
+static void
+add_triededns(fetchctx_t *fctx, isc_sockaddr_t *address) {
+ struct tried *tried;
+
+ tried = triededns(fctx, address);
+ if (tried != NULL) {
+ tried->count++;
+ return;
+ }
+
+ tried = isc_mem_get(fctx->mctx, sizeof(*tried));
+ if (tried == NULL)
+ return;
+
+ tried->addr = *address;
+ tried->count = 1;
+ ISC_LIST_INITANDAPPEND(fctx->edns, tried, link);
+}
+
+static struct tried *
+triededns512(fetchctx_t *fctx, isc_sockaddr_t *address) {
+ struct tried *tried;
+
+ for (tried = ISC_LIST_HEAD(fctx->edns512);
+ tried != NULL;
+ tried = ISC_LIST_NEXT(tried, link)) {
+ if (isc_sockaddr_equal(&tried->addr, address))
+ return (tried);
+ }
+
+ return (NULL);
+}
+
+static void
+add_triededns512(fetchctx_t *fctx, isc_sockaddr_t *address) {
+ struct tried *tried;
+
+ tried = triededns512(fctx, address);
+ if (tried != NULL) {
+ tried->count++;
+ return;
+ }
+
+ tried = isc_mem_get(fctx->mctx, sizeof(*tried));
+ if (tried == NULL)
+ return;
+
+ tried->addr = *address;
+ tried->count = 1;
+ ISC_LIST_INITANDAPPEND(fctx->edns512, tried, link);
+}
+
+static void
+compute_cc(resquery_t *query, unsigned char *cookie, size_t len) {
+#ifdef AES_CC
+ unsigned char digest[ISC_AES_BLOCK_LENGTH];
+ unsigned char input[16];
+ isc_netaddr_t netaddr;
+ unsigned int i;
+
+ INSIST(len >= 8U);
+
+ isc_netaddr_fromsockaddr(&netaddr, &query->addrinfo->sockaddr);
+ switch (netaddr.family) {
+ case AF_INET:
+ memmove(input, (unsigned char *)&netaddr.type.in, 4);
+ memset(input + 4, 0, 12);
+ break;
+ case AF_INET6:
+ memmove(input, (unsigned char *)&netaddr.type.in6, 16);
+ break;
+ }
+ isc_aes128_crypt(query->fctx->res->view->secret, input, digest);
+ for (i = 0; i < 8; i++)
+ digest[i] ^= digest[i + 8];
+ memmove(cookie, digest, 8);
+#endif
+#ifdef HMAC_SHA1_CC
+ unsigned char digest[ISC_SHA1_DIGESTLENGTH];
+ isc_netaddr_t netaddr;
+ isc_hmacsha1_t hmacsha1;
+
+ INSIST(len >= 8U);
+
+ isc_hmacsha1_init(&hmacsha1, query->fctx->res->view->secret,
+ ISC_SHA1_DIGESTLENGTH);
+ isc_netaddr_fromsockaddr(&netaddr, &query->addrinfo->sockaddr);
+ switch (netaddr.family) {
+ case AF_INET:
+ isc_hmacsha1_update(&hmacsha1,
+ (unsigned char *)&netaddr.type.in, 4);
+ break;
+ case AF_INET6:
+ isc_hmacsha1_update(&hmacsha1,
+ (unsigned char *)&netaddr.type.in6, 16);
+ break;
+ }
+ isc_hmacsha1_sign(&hmacsha1, digest, sizeof(digest));
+ memmove(cookie, digest, 8);
+ isc_hmacsha1_invalidate(&hmacsha1);
+#endif
+#ifdef HMAC_SHA256_CC
+ unsigned char digest[ISC_SHA256_DIGESTLENGTH];
+ isc_netaddr_t netaddr;
+ isc_hmacsha256_t hmacsha256;
+
+ INSIST(len >= 8U);
+
+ isc_hmacsha256_init(&hmacsha256, query->fctx->res->view->secret,
+ ISC_SHA256_DIGESTLENGTH);
+ isc_netaddr_fromsockaddr(&netaddr, &query->addrinfo->sockaddr);
+ switch (netaddr.family) {
+ case AF_INET:
+ isc_hmacsha256_update(&hmacsha256,
+ (unsigned char *)&netaddr.type.in, 4);
+ break;
+ case AF_INET6:
+ isc_hmacsha256_update(&hmacsha256,
+ (unsigned char *)&netaddr.type.in6, 16);
+ break;
+ }
+ isc_hmacsha256_sign(&hmacsha256, digest, sizeof(digest));
+ memmove(cookie, digest, 8);
+ isc_hmacsha256_invalidate(&hmacsha256);
+#endif
+}
+
+static isc_result_t
+issecuredomain(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type,
+ isc_stdtime_t now, bool checknta,
+ bool *issecure)
+{
+ dns_name_t suffix;
+ unsigned int labels;
+
+ /*
+ * For DS variants we need to check fom the parent domain,
+ * since there may be a negative trust anchor for the name,
+ * while the enclosing domain where the DS record lives is
+ * under a secure entry point.
+ */
+ labels = dns_name_countlabels(name);
+ if (dns_rdatatype_atparent(type) && labels > 1) {
+ dns_name_init(&suffix, NULL);
+ dns_name_getlabelsequence(name, 1, labels - 1, &suffix);
+ name = &suffix;
+ }
+
+ return (dns_view_issecuredomain(view, name, now, checknta, issecure));
+}
+
+static bool
+wouldvalidate(fetchctx_t *fctx) {
+ bool secure_domain;
+ isc_result_t result;
+ isc_stdtime_t now;
+
+ if (!fctx->res->view->enablevalidation)
+ return (false);
+
+ if (fctx->res->view->dlv != NULL)
+ return (true);
+
+ isc_stdtime_get(&now);
+ result = dns_view_issecuredomain(fctx->res->view, &fctx->name,
+ now, true, &secure_domain);
+ if (result != ISC_R_SUCCESS)
+ return (false);
+ return (secure_domain);
+}
+
+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 *sock;
+ 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;
+ bool useedns;
+ dns_compress_t cctx;
+ bool cleanup_cctx = false;
+ bool secure_domain;
+ bool connecting = false;
+ bool tcp = (query->options & DNS_FETCHOPT_TCP);
+ dns_ednsopt_t ednsopts[DNS_EDNSOPTIONS];
+ unsigned ednsopt = 0;
+ uint16_t hint = 0, udpsize = 0; /* No EDNS */
+#ifdef HAVE_DNSTAP
+ isc_sockaddr_t localaddr, *la = NULL;
+ unsigned char zone[DNS_NAME_MAXWIRE];
+ dns_dtmsgtype_t dtmsgtype;
+ isc_region_t zr;
+ isc_buffer_t zb;
+#endif /* HAVE_DNSTAP */
+
+ fctx = query->fctx;
+ QTRACE("send");
+
+ res = fctx->res;
+ task = res->buckets[fctx->bucketnum].task;
+ address = NULL;
+
+ if (tcp) {
+ /*
+ * 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_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 not to validate, or if the
+ * question is under a secure entry point and this is a
+ * recursive/forward query -- unless the client said not to.
+ */
+ if ((query->options & DNS_FETCHOPT_NOCDFLAG) != 0)
+ /* Do nothing */
+ ;
+ else if ((query->options & DNS_FETCHOPT_NOVALIDATE) != 0)
+ fctx->qmessage->flags |= DNS_MESSAGEFLAG_CD;
+ else if (res->view->enablevalidation &&
+ ((fctx->qmessage->flags & DNS_MESSAGEFLAG_RD) != 0))
+ {
+ bool checknta = !(query->options & DNS_FETCHOPT_NONTA);
+ result = issecuredomain(res->view, &fctx->name, fctx->type,
+ isc_time_seconds(&query->start),
+ checknta, &secure_domain);
+ if (result != ISC_R_SUCCESS)
+ secure_domain = false;
+ if (res->view->dlv != NULL)
+ secure_domain = 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 = 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;
+
+ /* See if response history indicates that EDNS is not supported. */
+ if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0 &&
+ dns_adb_noedns(fctx->adb, query->addrinfo))
+ query->options |= DNS_FETCHOPT_NOEDNS0;
+
+ if (fctx->timeout && (query->options & DNS_FETCHOPT_NOEDNS0) == 0) {
+ isc_sockaddr_t *sockaddr = &query->addrinfo->sockaddr;
+ struct tried *tried;
+
+ if (fctx->timeouts > (MAX_EDNS0_TIMEOUTS * 2) &&
+ (!EDNSOK(query->addrinfo) || !wouldvalidate(fctx))) {
+ query->options |= DNS_FETCHOPT_NOEDNS0;
+ fctx->reason = "disabling EDNS";
+ } else if ((tried = triededns512(fctx, sockaddr)) != NULL &&
+ tried->count >= 2U &&
+ (!EDNSOK(query->addrinfo) || !wouldvalidate(fctx))) {
+ query->options |= DNS_FETCHOPT_NOEDNS0;
+ fctx->reason = "disabling EDNS";
+ } else if ((tried = triededns(fctx, sockaddr)) != NULL) {
+ if (tried->count == 1U) {
+ hint = dns_adb_getudpsize(fctx->adb,
+ query->addrinfo);
+ } else if (tried->count >= 2U) {
+ query->options |= DNS_FETCHOPT_EDNS512;
+ fctx->reason = "reducing the advertised EDNS "
+ "UDP packet size to 512 octets";
+ }
+ }
+ }
+ fctx->timeout = false;
+
+ /*
+ * Use EDNS0, unless the caller doesn't want it, or we know that
+ * the remote server doesn't like it.
+ */
+ if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0) {
+ if ((query->addrinfo->flags & DNS_FETCHOPT_NOEDNS0) == 0) {
+ unsigned int version = DNS_EDNS_VERSION;
+ unsigned int flags = query->addrinfo->flags;
+ bool reqnsid = res->view->requestnsid;
+ bool sendcookie = res->view->sendcookie;
+ unsigned char cookie[64];
+
+ if ((flags & FCTX_ADDRINFO_EDNSOK) != 0 &&
+ (query->options & DNS_FETCHOPT_EDNS512) == 0) {
+ udpsize = dns_adb_probesize2(fctx->adb,
+ query->addrinfo,
+ fctx->timeouts);
+ if (udpsize > res->udpsize)
+ udpsize = res->udpsize;
+ }
+
+ if (peer != NULL)
+ (void)dns_peer_getudpsize(peer, &udpsize);
+
+ if (udpsize == 0U && res->udpsize == 512U)
+ udpsize = 512;
+
+ /*
+ * Was the size forced to 512 in the configuration?
+ */
+ if (udpsize == 512U)
+ query->options |= DNS_FETCHOPT_EDNS512;
+
+ /*
+ * We have talked to this server before.
+ */
+ if (hint != 0U)
+ udpsize = hint;
+
+ /*
+ * We know nothing about the peer's capabilities
+ * so start with minimal EDNS UDP size.
+ */
+ if (udpsize == 0U)
+ udpsize = 512;
+
+ if ((flags & DNS_FETCHOPT_EDNSVERSIONSET) != 0) {
+ version = flags & DNS_FETCHOPT_EDNSVERSIONMASK;
+ version >>= DNS_FETCHOPT_EDNSVERSIONSHIFT;
+ }
+
+ /* Request NSID/COOKIE/VERSION for current peer? */
+ if (peer != NULL) {
+ uint8_t ednsversion;
+ (void) dns_peer_getrequestnsid(peer, &reqnsid);
+ (void) dns_peer_getsendcookie(peer,
+ &sendcookie);
+ result = dns_peer_getednsversion(peer,
+ &ednsversion);
+ if (result == ISC_R_SUCCESS &&
+ ednsversion < version)
+ version = ednsversion;
+ }
+ if (NOCOOKIE(query->addrinfo))
+ sendcookie = false;
+ if (reqnsid) {
+ INSIST(ednsopt < DNS_EDNSOPTIONS);
+ ednsopts[ednsopt].code = DNS_OPT_NSID;
+ ednsopts[ednsopt].length = 0;
+ ednsopts[ednsopt].value = NULL;
+ ednsopt++;
+ }
+#if DNS_EDNS_VERSION > 0
+ /*
+ * Some EDNS(0) servers don't ignore unknown options
+ * as it was not a explict requirement of RFC 2671.
+ * Only send COOKIE to EDNS(1) servers.
+ */
+ if (version < 1)
+ sendcookie = false;
+#endif
+ if (sendcookie) {
+ INSIST(ednsopt < DNS_EDNSOPTIONS);
+ ednsopts[ednsopt].code = DNS_OPT_COOKIE;
+ ednsopts[ednsopt].length = (uint16_t)
+ dns_adb_getcookie(fctx->adb,
+ query->addrinfo,
+ cookie,
+ sizeof(cookie));
+ if (ednsopts[ednsopt].length != 0) {
+ ednsopts[ednsopt].value = cookie;
+ inc_stats(fctx->res,
+ dns_resstatscounter_cookieout);
+ } else {
+ compute_cc(query, cookie, 8);
+ ednsopts[ednsopt].value = cookie;
+ ednsopts[ednsopt].length = 8;
+ inc_stats(fctx->res,
+ dns_resstatscounter_cookienew);
+ }
+ ednsopt++;
+ }
+ query->ednsversion = version;
+ result = fctx_addopt(fctx->qmessage, version,
+ udpsize, ednsopts, ednsopt);
+ 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;
+ query->ednsversion = -1;
+ udpsize = 0;
+ }
+ } 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;
+ query->ednsversion = -1;
+ }
+ } else
+ query->ednsversion = -1;
+
+ /*
+ * Record the UDP EDNS size choosen.
+ */
+ query->udpsize = udpsize;
+
+ /*
+ * 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 (udpsize > 512U)
+ add_triededns(fctx, &query->addrinfo->sockaddr);
+
+ if (udpsize == 512U)
+ 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;
+
+#ifdef HAVE_DNSTAP
+ memset(&zr, 0, sizeof(zr));
+ isc_buffer_init(&zb, zone, sizeof(zone));
+ dns_compress_setmethods(&cctx, DNS_COMPRESS_NONE);
+ result = dns_name_towire(&fctx->domain, &cctx, &zb);
+ if (result == ISC_R_SUCCESS)
+ isc_buffer_usedregion(&zb, &zr);
+#endif /* HAVE_DNSTAP */
+
+ dns_compress_invalidate(&cctx);
+ cleanup_cctx = 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 (tcp) {
+ isc_buffer_usedregion(&query->buffer, &r);
+ isc_buffer_putuint16(&tcpbuffer, (uint16_t)r.length);
+ isc_buffer_add(&tcpbuffer, r.length);
+ }
+
+ /*
+ * Log the outgoing packet.
+ */
+ dns_message_logfmtpacket2(fctx->qmessage, "sending packet to",
+ &query->addrinfo->sockaddr,
+ DNS_LOGCATEGORY_RESOLVER,
+ DNS_LOGMODULE_PACKETS,
+ &dns_master_style_comment,
+ ISC_LOG_DEBUG(11),
+ fctx->res->mctx);
+
+ /*
+ * We're now done with the query message.
+ */
+ dns_message_reset(fctx->qmessage, DNS_MESSAGE_INTENTRENDER);
+
+ if (query->exclusivesocket)
+ sock = dns_dispatch_getentrysocket(query->dispentry);
+ else
+ sock = dns_dispatch_getsocket(query->dispatch);
+ /*
+ * Send the query!
+ */
+ if (!tcp) {
+ address = &query->addrinfo->sockaddr;
+ if (query->exclusivesocket) {
+ result = isc_socket_connect(sock, address, task,
+ resquery_udpconnected,
+ query);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_message;
+ connecting = true;
+ 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.
+ */
+ memset(&query->sendevent, 0, sizeof(query->sendevent));
+ ISC_EVENT_INIT(&query->sendevent, sizeof(query->sendevent), 0, NULL,
+ ISC_SOCKEVENT_SENDDONE, resquery_senddone, query,
+ NULL, NULL, NULL);
+
+ if (query->dscp == -1) {
+ query->sendevent.attributes &= ~ISC_SOCKEVENTATTR_DSCP;
+ query->sendevent.dscp = 0;
+ } else {
+ query->sendevent.attributes |= ISC_SOCKEVENTATTR_DSCP;
+ query->sendevent.dscp = query->dscp;
+ if (tcp)
+ isc_socket_dscp(sock, query->dscp);
+ }
+
+ result = isc_socket_sendto2(sock, &r, task, address, NULL,
+ &query->sendevent, 0);
+ if (result != ISC_R_SUCCESS) {
+ if (connecting) {
+ /*
+ * This query is still connecting.
+ * Mark it as canceled so that it will just be
+ * cleaned up when the connected event is received.
+ * Keep fctx around until the event is processed.
+ */
+ query->fctx->nqueries++;
+ query->attributes |= RESQUERY_ATTR_CANCELED;
+ }
+ goto cleanup_message;
+ }
+
+ query->sends++;
+
+ QTRACE("sent");
+
+#ifdef HAVE_DNSTAP
+ /*
+ * Log the outgoing query via dnstap.
+ */
+ if ((fctx->qmessage->flags & DNS_MESSAGEFLAG_RD) != 0)
+ dtmsgtype = DNS_DTTYPE_FQ;
+ else
+ dtmsgtype = DNS_DTTYPE_RQ;
+
+ result = isc_socket_getsockname(sock, &localaddr);
+ if (result == ISC_R_SUCCESS)
+ la = &localaddr;
+
+ dns_dt_send(fctx->res->view, dtmsgtype, la, &query->addrinfo->sockaddr,
+ tcp, &zr, &query->start, NULL, &query->buffer);
+#endif /* HAVE_DNSTAP */
+
+ 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;
+ bool retry = 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) {
+ FCTXTRACE("query canceled: idle timer failed; "
+ "responding");
+
+ fctx_cancelquery(&query, NULL, NULL, false,
+ false);
+ fctx_done(fctx, result, __LINE__);
+ 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) {
+ FCTXTRACE("query canceled: "
+ "resquery_send() failed; responding");
+
+ fctx_cancelquery(&query, NULL, NULL, false, false);
+ fctx_done(fctx, result, __LINE__);
+ }
+ 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:
+ FCTXTRACE3("query canceled in connected(): "
+ "no route to host; no response",
+ sevent->result);
+
+ /*
+ * No route to remote.
+ */
+ isc_socket_detach(&query->tcpsocket);
+ fctx_cancelquery(&query, NULL, NULL, true, false);
+ retry = true;
+ break;
+
+ default:
+ FCTXTRACE3("query canceled in connected() due to "
+ "unexpected event result; responding",
+ sevent->result);
+
+ isc_socket_detach(&query->tcpsocket);
+ fctx_cancelquery(&query, NULL, NULL, false, 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, __LINE__);
+ else
+ fctx_try(fctx, true, false);
+ }
+}
+
+static void
+fctx_finddone(isc_task_t *task, isc_event_t *event) {
+ fetchctx_t *fctx;
+ dns_adbfind_t *find;
+ dns_resolver_t *res;
+ bool want_try = false;
+ bool want_done = false;
+ bool bucket_empty = false;
+ unsigned int bucketnum;
+ bool dodestroy = false;
+
+ find = event->ev_sender;
+ fctx = event->ev_arg;
+ REQUIRE(VALID_FCTX(fctx));
+ res = fctx->res;
+
+ UNUSED(task);
+
+ FCTXTRACE("finddone");
+
+ bucketnum = fctx->bucketnum;
+ LOCK(&res->buckets[bucketnum].lock);
+
+ INSIST(fctx->pending > 0);
+ fctx->pending--;
+
+ if (ADDRWAIT(fctx)) {
+ /*
+ * The fetch is waiting for a name to be found.
+ */
+ INSIST(!SHUTTINGDOWN(fctx));
+ if (event->ev_type == DNS_EVENT_ADBMOREADDRESSES) {
+ fctx->attributes &= ~FCTX_ATTR_ADDRWAIT;
+ want_try = true;
+ } else {
+ fctx->findfail++;
+ 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.
+ */
+ fctx->attributes &= ~FCTX_ATTR_ADDRWAIT;
+ want_done = true;
+ }
+ }
+ } else if (SHUTTINGDOWN(fctx) && fctx->pending == 0 &&
+ fctx->nqueries == 0 && ISC_LIST_EMPTY(fctx->validators)) {
+
+ if (fctx->references == 0) {
+ bucket_empty = fctx_unlink(fctx);
+ dodestroy = true;
+ }
+ }
+ UNLOCK(&res->buckets[bucketnum].lock);
+
+ isc_event_free(&event);
+ dns_adb_destroyfind(&find);
+
+ if (want_try) {
+ fctx_try(fctx, true, false);
+ } else if (want_done) {
+ FCTXTRACE("fetch failed in finddone(); return ISC_R_FAILURE");
+ fctx_done(fctx, ISC_R_FAILURE, __LINE__);
+ } else if (dodestroy) {
+ fctx_destroy(fctx);
+ if (bucket_empty)
+ empty_bucket(res);
+ }
+}
+
+
+static inline bool
+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 (true);
+ }
+
+ return (false);
+}
+
+static inline bool
+mark_bad(fetchctx_t *fctx) {
+ dns_adbfind_t *curr;
+ dns_adbaddrinfo_t *addrinfo;
+ bool all_bad = true;
+
+#ifdef ENABLE_AFL
+ if (dns_fuzzing_resolver)
+ return false;
+#endif
+
+ /*
+ * 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 = 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 = 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 = 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 = false;
+ }
+
+ return (all_bad);
+}
+
+static void
+add_bad(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, isc_result_t reason,
+ badnstype_t badtype)
+{
+ 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 *spc = "";
+ isc_sockaddr_t *address = &addrinfo->sockaddr;
+
+#ifdef ENABLE_AFL
+ if (dns_fuzzing_resolver)
+ return;
+#endif
+
+ if (reason == DNS_R_LAME)
+ fctx->lamecount++;
+ else {
+ switch (badtype) {
+ case badns_unreachable:
+ fctx->neterr++;
+ break;
+ case badns_response:
+ fctx->badresp++;
+ break;
+ case badns_validation:
+ break; /* counted as 'valfail' */
+ }
+ }
+
+ if (bad_server(fctx, address)) {
+ /*
+ * We already know this server is bad.
+ */
+ return;
+ }
+
+ FCTXTRACE("add_bad");
+
+ sa = isc_mem_get(fctx->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';
+ spc = " ";
+ } 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';
+ spc = " ";
+ } else {
+ code[0] = '\0';
+ }
+ 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 resolving '%s/%s/%s': %s",
+ code, spc, dns_result_totext(reason),
+ namebuf, typebuf, classbuf, addrbuf);
+}
+
+/*
+ * Sort addrinfo list by RTT.
+ */
+static void
+sort_adbfind(dns_adbfind_t *find, unsigned int bias) {
+ dns_adbaddrinfo_t *best, *curr;
+ dns_adbaddrinfolist_t sorted;
+ unsigned int best_srtt, curr_srtt;
+
+ /* Lame N^2 bubble sort. */
+ ISC_LIST_INIT(sorted);
+ while (!ISC_LIST_EMPTY(find->list)) {
+ best = ISC_LIST_HEAD(find->list);
+ best_srtt = best->srtt;
+ if (isc_sockaddr_pf(&best->sockaddr) != AF_INET6)
+ best_srtt += bias;
+ curr = ISC_LIST_NEXT(best, publink);
+ while (curr != NULL) {
+ curr_srtt = curr->srtt;
+ if (isc_sockaddr_pf(&curr->sockaddr) != AF_INET6)
+ curr_srtt += bias;
+ if (curr_srtt < best_srtt) {
+ best = curr;
+ best_srtt = curr_srtt;
+ }
+ 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.
+ */
+static void
+sort_finds(dns_adbfindlist_t *findlist, unsigned int bias) {
+ dns_adbfind_t *best, *curr;
+ dns_adbfindlist_t sorted;
+ dns_adbaddrinfo_t *addrinfo, *bestaddrinfo;
+ unsigned int best_srtt, curr_srtt;
+
+ /* Sort each find's addrinfo list by SRTT. */
+ for (curr = ISC_LIST_HEAD(*findlist);
+ curr != NULL;
+ curr = ISC_LIST_NEXT(curr, publink))
+ sort_adbfind(curr, bias);
+
+ /* 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);
+ best_srtt = bestaddrinfo->srtt;
+ if (isc_sockaddr_pf(&bestaddrinfo->sockaddr) != AF_INET6)
+ best_srtt += bias;
+ curr = ISC_LIST_NEXT(best, publink);
+ while (curr != NULL) {
+ addrinfo = ISC_LIST_HEAD(curr->list);
+ INSIST(addrinfo != NULL);
+ curr_srtt = addrinfo->srtt;
+ if (isc_sockaddr_pf(&addrinfo->sockaddr) != AF_INET6)
+ curr_srtt += bias;
+ if (curr_srtt < best_srtt) {
+ best = curr;
+ best_srtt = curr_srtt;
+ }
+ 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,
+ bool *overquota, bool *need_alternate)
+{
+ dns_adbaddrinfo_t *ai;
+ dns_adbfind_t *find;
+ dns_resolver_t *res;
+ bool unshared;
+ isc_result_t result;
+
+ res = fctx->res;
+ unshared = (fctx->options & DNS_FETCHOPT_UNSHARED);
+ /*
+ * 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_createfind2(fctx->adb,
+ res->buckets[fctx->bucketnum].task,
+ fctx_finddone, fctx, name,
+ &fctx->name, fctx->type,
+ options, now, NULL,
+ res->view->dstport,
+ fctx->depth + 1, fctx->qc, &find);
+ if (result != ISC_R_SUCCESS) {
+ if (result == DNS_R_ALIAS) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+
+ /*
+ * XXXRTH Follow the CNAME/DNAME chain?
+ */
+ dns_adb_destroyfind(&find);
+ fctx->adberr++;
+ dns_name_format(name, namebuf, sizeof(namebuf));
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_CNAME,
+ DNS_LOGMODULE_RESOLVER, ISC_LOG_INFO,
+ "skipping nameserver '%s' because it "
+ "is a CNAME, while resolving '%s'",
+ namebuf, fctx->info);
+ }
+ } 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->dispatches4 == NULL &&
+ find->result_v6 != DNS_R_NXDOMAIN) ||
+ (res->dispatches6 == NULL &&
+ find->result_v4 != DNS_R_NXDOMAIN)))
+ *need_alternate = true;
+ } else {
+ if ((find->options & DNS_ADBFIND_OVERQUOTA) != 0) {
+ if (overquota != NULL)
+ *overquota = true;
+ fctx->quotacount++; /* quota exceeded */
+ }
+ else if ((find->options & DNS_ADBFIND_LAMEPRUNED) != 0)
+ fctx->lamecount++; /* cached lame server */
+ else
+ fctx->adberr++; /* unreachable server, etc. */
+
+ /*
+ * 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->dispatches4 == NULL &&
+ find->result_v6 == DNS_R_NXRRSET) ||
+ (res->dispatches6 == NULL &&
+ find->result_v4 == DNS_R_NXRRSET)))
+ *need_alternate = true;
+ dns_adb_destroyfind(&find);
+ }
+ }
+}
+
+static bool
+isstrictsubdomain(dns_name_t *name1, dns_name_t *name2) {
+ int order;
+ unsigned int nlabels;
+ dns_namereln_t namereln;
+
+ namereln = dns_name_fullcompare(name1, name2, &order, &nlabels);
+ return (namereln == dns_namereln_subdomain);
+}
+
+static isc_result_t
+fctx_getaddresses(fetchctx_t *fctx, bool badcache) {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_result_t result;
+ dns_resolver_t *res;
+ isc_stdtime_t now;
+ unsigned int stdoptions = 0;
+ dns_forwarder_t *fwd;
+ dns_adbaddrinfo_t *ai;
+ bool all_bad;
+ dns_rdata_ns_t ns;
+ bool need_alternate = false;
+ bool all_spilled = true;
+
+ FCTXTRACE5("getaddresses", "fctx->depth=", fctx->depth);
+
+ /*
+ * Don't pound on remote servers. (Failsafe!)
+ */
+ fctx->restarts++;
+ if (fctx->restarts > 100) {
+ FCTXTRACE("too many restarts");
+ return (DNS_R_SERVFAIL);
+ }
+
+ res = fctx->res;
+
+ if (fctx->depth > res->maxdepth) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
+ DNS_LOGMODULE_RESOLVER, ISC_LOG_DEBUG(3),
+ "too much NS indirection resolving '%s' "
+ "(depth=%u, maxdepth=%u)",
+ fctx->info, fctx->depth, res->maxdepth);
+ return (DNS_R_SERVFAIL);
+ }
+
+ /*
+ * 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).
+ */
+ fwd = ISC_LIST_HEAD(fctx->forwarders);
+ if (fwd == NULL) {
+ dns_forwarders_t *forwarders = NULL;
+ dns_name_t *name = &fctx->name;
+ dns_name_t suffix;
+ unsigned int labels;
+ dns_fixedname_t fixed;
+ dns_name_t *domain;
+
+ /*
+ * DS records are found in the parent server.
+ * Strip label to get the correct forwarder (if any).
+ */
+ if (dns_rdatatype_atparent(fctx->type) &&
+ 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;
+ }
+
+ domain = dns_fixedname_initname(&fixed);
+ result = dns_fwdtable_find2(res->view->fwdtable, name,
+ domain, &forwarders);
+ if (result == ISC_R_SUCCESS) {
+ fwd = ISC_LIST_HEAD(forwarders->fwdrs);
+ fctx->fwdpolicy = forwarders->fwdpolicy;
+ if (fctx->fwdpolicy == dns_fwdpolicy_only &&
+ isstrictsubdomain(domain, &fctx->domain)) {
+ fcount_decr(fctx);
+ dns_name_free(&fctx->domain, fctx->mctx);
+ dns_name_init(&fctx->domain, NULL);
+ result = dns_name_dup(domain, fctx->mctx,
+ &fctx->domain);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ result = fcount_incr(fctx, true);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+ }
+ }
+
+ while (fwd != NULL) {
+ if ((isc_sockaddr_pf(&fwd->addr) == AF_INET &&
+ res->dispatches4 == NULL) ||
+ (isc_sockaddr_pf(&fwd->addr) == AF_INET6 &&
+ res->dispatches6 == NULL))
+ {
+ fwd = ISC_LIST_NEXT(fwd, link);
+ continue;
+ }
+ ai = NULL;
+ result = dns_adb_findaddrinfo(fctx->adb, &fwd->addr, &ai, 0);
+ if (result == ISC_R_SUCCESS) {
+ dns_adbaddrinfo_t *cur;
+ ai->flags |= FCTX_ADDRINFO_FORWARDER;
+ ai->dscp = fwd->dscp;
+ 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);
+ }
+ fwd = ISC_LIST_NEXT(fwd, 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->dispatches4 != NULL)
+ stdoptions |= DNS_ADBFIND_INET;
+ if (res->dispatches6 != NULL)
+ stdoptions |= DNS_ADBFIND_INET6;
+
+ if ((stdoptions & DNS_ADBFIND_ADDRESSMASK) == 0)
+ return (DNS_R_SERVFAIL);
+
+ 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))
+ {
+ bool overquota = false;
+
+ 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,
+ &overquota, &need_alternate);
+
+ if (!overquota)
+ all_spilled = false;
+
+ 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->dispatches6 != NULL) ? AF_INET6 : AF_INET;
+ for (a = ISC_LIST_HEAD(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, 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 {
+ isc_time_t expire;
+ isc_interval_t i;
+ /*
+ * We've lost completely. We don't know any
+ * addresses, and the ADB has told us it can't get
+ * them.
+ */
+ FCTXTRACE("no addresses");
+ isc_interval_set(&i, DNS_RESOLVER_BADCACHETTL(fctx), 0);
+ result = isc_time_nowplusinterval(&expire, &i);
+ if (badcache &&
+ (fctx->type == dns_rdatatype_dnskey ||
+ fctx->type == dns_rdatatype_dlv ||
+ fctx->type == dns_rdatatype_ds) &&
+ result == ISC_R_SUCCESS)
+ dns_resolver_addbadcache(res, &fctx->name,
+ fctx->type, &expire);
+
+ result = ISC_R_FAILURE;
+
+ /*
+ * If all of the addresses found were over the
+ * fetches-per-server quota, return the configured
+ * response.
+ */
+ if (all_spilled) {
+ result = res->quotaresp[dns_quotatype_server];
+ inc_stats(res, dns_resstatscounter_serverquota);
+ }
+ }
+ } else {
+ /*
+ * We've found some addresses. We might still be looking
+ * for more addresses.
+ */
+ sort_finds(&fctx->finds, res->view->v6bias);
+ sort_finds(&fctx->altfinds, 0);
+ 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;
+ bool aborted = false;
+ bool 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 = true;
+ }
+
+ if (peer != NULL &&
+ dns_peer_getbogus(peer, &bogus) == ISC_R_SUCCESS &&
+ bogus)
+ aborted = true;
+
+ if (aborted) {
+ addr->flags |= FCTX_ADDRINFO_MARK;
+ msg = "ignoring blackholed / bogus server: ";
+ } else if (isc_sockaddr_isnetzero(sa)) {
+ addr->flags |= FCTX_ADDRINFO_MARK;
+ msg = "ignoring net zero address: ";
+ } 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))) {
+ 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, bool retrying, bool badcache) {
+ isc_result_t result;
+ dns_adbaddrinfo_t *addrinfo = NULL;
+ dns_resolver_t *res;
+ unsigned int bucketnum;
+ bool bucket_empty;
+
+ FCTXTRACE5("try", "fctx->qc=", isc_counter_used(fctx->qc));
+
+ REQUIRE(!ADDRWAIT(fctx));
+
+ res = fctx->res;
+
+ /* We've already exceeded maximum query count */
+ if (isc_counter_used(fctx->qc) > res->maxqueries) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
+ DNS_LOGMODULE_RESOLVER, ISC_LOG_DEBUG(3),
+ "exceeded max queries resolving '%s' "
+ "(querycount=%u, maxqueries=%u)",
+ fctx->info,
+ isc_counter_used(fctx->qc), res->maxqueries);
+ fctx_done(fctx, DNS_R_SERVFAIL, __LINE__);
+ return;
+ }
+
+ addrinfo = fctx_nextaddress(fctx);
+
+ /* Try to find an address that isn't over quota */
+ while (addrinfo != NULL && dns_adbentry_overquota(addrinfo->entry))
+ addrinfo = fctx_nextaddress(fctx);
+
+ if (addrinfo == NULL) {
+ /* We have no more addresses. Start over. */
+ fctx_cancelqueries(fctx, true, false);
+ fctx_cleanupall(fctx);
+ result = fctx_getaddresses(fctx, badcache);
+ 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, __LINE__);
+ return;
+ }
+
+ addrinfo = fctx_nextaddress(fctx);
+
+ while (addrinfo != NULL &&
+ dns_adbentry_overquota(addrinfo->entry))
+ 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, __LINE__);
+ return;
+ }
+ }
+
+ if (dns_name_countlabels(&fctx->domain) > 2) {
+ result = isc_counter_increment(fctx->qc);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
+ DNS_LOGMODULE_RESOLVER, ISC_LOG_DEBUG(3),
+ "exceeded max queries resolving '%s'",
+ fctx->info);
+ fctx_done(fctx, DNS_R_SERVFAIL, __LINE__);
+ return;
+ }
+ }
+
+ bucketnum = fctx->bucketnum;
+ fctx_increference(fctx);
+ result = fctx_query(fctx, addrinfo, fctx->options);
+ if (result != ISC_R_SUCCESS) {
+ fctx_done(fctx, result, __LINE__);
+ LOCK(&res->buckets[bucketnum].lock);
+ bucket_empty = fctx_decreference(fctx);
+ UNLOCK(&res->buckets[bucketnum].lock);
+ if (bucket_empty)
+ empty_bucket(res);
+ } else if (retrying)
+ inc_stats(res, dns_resstatscounter_retry);
+}
+
+static bool
+fctx_unlink(fetchctx_t *fctx) {
+ dns_resolver_t *res;
+ unsigned int bucketnum;
+
+ /*
+ * 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("unlink");
+
+ res = fctx->res;
+ bucketnum = fctx->bucketnum;
+
+ ISC_LIST_UNLINK(res->buckets[bucketnum].fctxs, fctx, link);
+
+ LOCK(&res->nlock);
+ res->nfctx--;
+ UNLOCK(&res->nlock);
+ dec_stats(res, dns_resstatscounter_nfetch);
+
+ if (res->buckets[bucketnum].exiting &&
+ ISC_LIST_EMPTY(res->buckets[bucketnum].fctxs))
+ return (true);
+
+ return (false);
+}
+
+static void
+fctx_destroy(fetchctx_t *fctx) {
+ isc_sockaddr_t *sa, *next_sa;
+ struct tried *tried;
+
+ 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));
+ REQUIRE(!ISC_LINK_LINKED(fctx, link));
+
+ FCTXTRACE("destroy");
+
+ /*
+ * 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(fctx->mctx, sa, sizeof(*sa));
+ }
+
+ for (tried = ISC_LIST_HEAD(fctx->edns);
+ tried != NULL;
+ tried = ISC_LIST_HEAD(fctx->edns)) {
+ ISC_LIST_UNLINK(fctx->edns, tried, link);
+ isc_mem_put(fctx->mctx, tried, sizeof(*tried));
+ }
+
+ for (tried = ISC_LIST_HEAD(fctx->edns512);
+ tried != NULL;
+ tried = ISC_LIST_HEAD(fctx->edns512)) {
+ ISC_LIST_UNLINK(fctx->edns512, tried, link);
+ isc_mem_put(fctx->mctx, tried, sizeof(*tried));
+ }
+
+ for (sa = ISC_LIST_HEAD(fctx->bad_edns);
+ sa != NULL;
+ sa = next_sa) {
+ next_sa = ISC_LIST_NEXT(sa, link);
+ ISC_LIST_UNLINK(fctx->bad_edns, sa, link);
+ isc_mem_put(fctx->mctx, sa, sizeof(*sa));
+ }
+
+ isc_counter_detach(&fctx->qc);
+ fcount_decr(fctx);
+ 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, fctx->mctx);
+ if (dns_rdataset_isassociated(&fctx->nameservers))
+ dns_rdataset_disassociate(&fctx->nameservers);
+ dns_name_free(&fctx->name, fctx->mctx);
+ dns_db_detach(&fctx->cache);
+ dns_adb_detach(&fctx->adb);
+ isc_mem_free(fctx->mctx, fctx->info);
+ isc_mem_putanddetach(&fctx->mctx, fctx, sizeof(*fctx));
+}
+
+/*
+ * 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");
+
+ inc_stats(fctx->res, dns_resstatscounter_querytimeout);
+
+ if (event->ev_type == ISC_TIMEREVENT_LIFE) {
+ fctx->reason = NULL;
+ fctx_done(fctx, ISC_R_TIMEDOUT, __LINE__);
+ } else {
+ isc_result_t result;
+
+ fctx->timeouts++;
+ fctx->timeout = true;
+
+ /*
+ * 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)
+ {
+ FCTXTRACE("query timed out; no response");
+ fctx_cancelquery(&query, NULL, NULL, true, false);
+ }
+ 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, __LINE__);
+ else
+ /*
+ * Keep trying.
+ */
+ fctx_try(fctx, true, false);
+ }
+
+ 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 = 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;
+ bool bucket_empty = false;
+ dns_resolver_t *res;
+ unsigned int bucketnum;
+ dns_validator_t *validator;
+ bool dodestroy = false;
+
+ 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 still running on behalf of this
+ * fetch, and clean up finds and addresses. To avoid deadlock
+ * with the ADB, we must do this before we lock the bucket lock.
+ */
+ fctx_stopqueries(fctx, false, false);
+ fctx_cleanupall(fctx);
+
+ 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, __LINE__);
+ }
+
+ if (fctx->references == 0 && fctx->pending == 0 &&
+ fctx->nqueries == 0 && ISC_LIST_EMPTY(fctx->validators)) {
+ bucket_empty = fctx_unlink(fctx);
+ dodestroy = true;
+ }
+
+ UNLOCK(&res->buckets[bucketnum].lock);
+
+ if (dodestroy) {
+ fctx_destroy(fctx);
+ if (bucket_empty)
+ empty_bucket(res);
+ }
+}
+
+static void
+fctx_start(isc_task_t *task, isc_event_t *event) {
+ fetchctx_t *fctx = event->ev_arg;
+ bool done = false, bucket_empty = false;
+ dns_resolver_t *res;
+ unsigned int bucketnum;
+ bool dodestroy = false;
+
+ 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, __LINE__);
+ /*
+ * 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_unlink(fctx);
+ dodestroy = true;
+ }
+ done = 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;
+
+ INSIST(!dodestroy);
+
+ /*
+ * All is well. Start working on the fetch.
+ */
+ result = fctx_starttimer(fctx);
+ if (result != ISC_R_SUCCESS)
+ fctx_done(fctx, result, __LINE__);
+ else
+ fctx_try(fctx, false, false);
+ } else if (dodestroy) {
+ fctx_destroy(fctx);
+ 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 *tclone;
+ 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.
+ */
+ tclone = NULL;
+ isc_task_attach(task, &tclone);
+ event = (dns_fetchevent_t *)
+ isc_event_allocate(fctx->res->mctx, tclone, DNS_EVENT_FETCHDONE,
+ action, arg, sizeof(*event));
+ if (event == NULL) {
+ isc_task_detach(&tclone);
+ 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++;
+ fctx->client = client;
+
+ fetch->magic = DNS_FETCH_MAGIC;
+ fetch->private = fctx;
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+log_ns_ttl(fetchctx_t *fctx, const char *where) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char domainbuf[DNS_NAME_FORMATSIZE];
+
+ dns_name_format(&fctx->name, namebuf, sizeof(namebuf));
+ dns_name_format(&fctx->domain, domainbuf, sizeof(domainbuf));
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
+ DNS_LOGMODULE_RESOLVER, ISC_LOG_DEBUG(10),
+ "log_ns_ttl: fctx %p: %s: %s (in '%s'?): %u %u",
+ fctx, where, namebuf, domainbuf,
+ fctx->ns_ttl_ok, fctx->ns_ttl);
+}
+
+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, unsigned int depth,
+ isc_counter_t *qc, 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];
+ isc_mem_t *mctx;
+
+ /*
+ * Caller must be holding the lock for bucket number 'bucketnum'.
+ */
+ REQUIRE(fctxp != NULL && *fctxp == NULL);
+
+ mctx = res->buckets[bucketnum].mctx;
+ fctx = isc_mem_get(mctx, sizeof(*fctx));
+ if (fctx == NULL)
+ return (ISC_R_NOMEMORY);
+
+ fctx->qc = NULL;
+ if (qc != NULL) {
+ isc_counter_attach(qc, &fctx->qc);
+ } else {
+ result = isc_counter_create(res->mctx,
+ res->maxqueries, &fctx->qc);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_fetch;
+ }
+
+ /*
+ * Make fctx->info point to a copy of a formatted string
+ * "name/type".
+ */
+ dns_name_format(name, buf, sizeof(buf));
+ dns_rdatatype_format(type, typebuf, sizeof(typebuf));
+ strlcat(buf, "/", sizeof(buf));
+ strlcat(buf, typebuf, sizeof(buf));
+ fctx->info = isc_mem_strdup(mctx, buf);
+ if (fctx->info == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup_counter;
+ }
+
+ FCTXTRACE("create");
+ dns_name_init(&fctx->name, NULL);
+ result = dns_name_dup(name, 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->dbucketnum = RES_NOBUCKET;
+ fctx->state = fetchstate_init;
+ fctx->want_shutdown = false;
+ fctx->cloned = false;
+ fctx->depth = depth;
+ 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->bad_edns);
+ ISC_LIST_INIT(fctx->validators);
+ fctx->validator = NULL;
+ fctx->find = NULL;
+ fctx->altfind = NULL;
+ fctx->pending = 0;
+ fctx->restarts = 0;
+ fctx->querysent = 0;
+ fctx->referrals = 0;
+ TIME_NOW(&fctx->start);
+ fctx->timeouts = 0;
+ fctx->lamecount = 0;
+ fctx->quotacount = 0;
+ fctx->adberr = 0;
+ fctx->neterr = 0;
+ fctx->badresp = 0;
+ fctx->findfail = 0;
+ fctx->valfail = 0;
+ fctx->result = ISC_R_FAILURE;
+ fctx->vresult = ISC_R_SUCCESS;
+ fctx->exitline = -1; /* sentinel */
+ fctx->logged = false;
+ fctx->attributes = 0;
+ fctx->spilled = false;
+ fctx->nqueries = 0;
+ fctx->reason = NULL;
+ fctx->rand_buf = 0;
+ fctx->rand_bits = 0;
+ fctx->timeout = false;
+ fctx->addrinfo = NULL;
+ fctx->client = NULL;
+ fctx->ns_ttl = 0;
+ fctx->ns_ttl_ok = false;
+
+ 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;
+ dns_name_t *fwdname = name;
+ dns_name_t suffix;
+
+ /*
+ * DS records are found in the parent server. Strip one
+ * leading label from the name (to be used in finding
+ * the forwarder).
+ */
+ if (dns_rdatatype_atparent(fctx->type) &&
+ dns_name_countlabels(name) > 1) {
+ dns_name_init(&suffix, NULL);
+ labels = dns_name_countlabels(name);
+ dns_name_getlabelsequence(name, 1, labels - 1, &suffix);
+ fwdname = &suffix;
+ }
+
+ /* Find the forwarder for this name. */
+ domain = dns_fixedname_initname(&fixed);
+ result = dns_fwdtable_find2(fctx->res->view->fwdtable, fwdname,
+ 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(fctx->type))
+ findoptions |= DNS_DBFIND_NOEXACT;
+ result = dns_view_findzonecut(res->view, name,
+ domain, 0, findoptions,
+ true,
+ &fctx->nameservers,
+ NULL);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_nameservers;
+
+ result = dns_name_dup(domain, mctx, &fctx->domain);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_nameservers;
+
+ fctx->ns_ttl = fctx->nameservers.ttl;
+ fctx->ns_ttl_ok = true;
+ } else {
+ /*
+ * We're in forward-only mode. Set the query domain.
+ */
+ result = dns_name_dup(domain, mctx, &fctx->domain);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_name;
+ }
+ } else {
+ result = dns_name_dup(domain, mctx, &fctx->domain);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_name;
+ dns_rdataset_clone(nameservers, &fctx->nameservers);
+ fctx->ns_ttl = fctx->nameservers.ttl;
+ fctx->ns_ttl_ok = true;
+ }
+
+ /*
+ * Are there too many simultaneous queries for this domain?
+ */
+ result = fcount_incr(fctx, false);
+ if (result != ISC_R_SUCCESS) {
+ result = fctx->res->quotaresp[dns_quotatype_zone];
+ inc_stats(res, dns_resstatscounter_zonequota);
+ goto cleanup_domain;
+ }
+
+ log_ns_ttl(fctx, "fctx_create");
+
+ INSIST(dns_name_issubdomain(&fctx->name, &fctx->domain));
+
+ fctx->qmessage = NULL;
+ result = dns_message_create(mctx, DNS_MESSAGE_INTENTRENDER,
+ &fctx->qmessage);
+
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_fcount;
+
+ fctx->rmessage = NULL;
+ result = dns_message_create(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, res->query_timeout, 0);
+ 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);
+ fctx->mctx = NULL;
+ isc_mem_attach(mctx, &fctx->mctx);
+
+ 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);
+ inc_stats(res, dns_resstatscounter_nfetch);
+
+ *fctxp = fctx;
+
+ return (ISC_R_SUCCESS);
+
+ cleanup_rmessage:
+ dns_message_destroy(&fctx->rmessage);
+
+ cleanup_qmessage:
+ dns_message_destroy(&fctx->qmessage);
+
+ cleanup_fcount:
+ fcount_decr(fctx);
+
+ cleanup_domain:
+ if (dns_name_countlabels(&fctx->domain) > 0)
+ dns_name_free(&fctx->domain, mctx);
+
+ cleanup_nameservers:
+ if (dns_rdataset_isassociated(&fctx->nameservers))
+ dns_rdataset_disassociate(&fctx->nameservers);
+
+ cleanup_name:
+ dns_name_free(&fctx->name, mctx);
+
+ cleanup_info:
+ isc_mem_free(mctx, fctx->info);
+
+ cleanup_counter:
+ isc_counter_detach(&fctx->qc);
+
+ cleanup_fetch:
+ isc_mem_put(mctx, fctx, sizeof(*fctx));
+
+ return (result);
+}
+
+/*
+ * Handle Responses
+ */
+static inline bool
+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_yxdomain &&
+ message->rcode != dns_rcode_nxdomain)
+ return (false);
+
+ if (message->counts[DNS_SECTION_ANSWER] != 0)
+ return (false);
+
+ if (message->counts[DNS_SECTION_AUTHORITY] == 0)
+ return (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 (false);
+ if (namereln == dns_namereln_subdomain)
+ return (false);
+ return (true);
+ }
+ result = dns_message_nextname(message, DNS_SECTION_AUTHORITY);
+ }
+
+ return (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 void
+log_formerr(fetchctx_t *fctx, const char *format, ...) {
+ char nsbuf[ISC_SOCKADDR_FORMATSIZE];
+ char clbuf[ISC_SOCKADDR_FORMATSIZE];
+ const char *clmsg = "";
+ char msgbuf[2048];
+ va_list args;
+
+ va_start(args, format);
+ vsnprintf(msgbuf, sizeof(msgbuf), format, args);
+ va_end(args);
+
+ isc_sockaddr_format(&fctx->addrinfo->sockaddr, nsbuf, sizeof(nsbuf));
+
+ if (fctx->client != NULL) {
+ clmsg = " for client ";
+ isc_sockaddr_format(fctx->client, clbuf, sizeof(clbuf));
+ } else {
+ clbuf[0] = '\0';
+ }
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
+ DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE,
+ "DNS format error from %s resolving %s%s%s: %s",
+ nsbuf, fctx->info, clmsg, clbuf, msgbuf);
+}
+
+static 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 (ISC_UNLIKELY(message->counts[DNS_SECTION_QUESTION] == 0)) {
+ if ((message->flags & DNS_MESSAGEFLAG_TC) != 0) {
+ /*
+ * If TC=1 and the question section is empty, we
+ * accept the reply message as a truncated
+ * answer, to be retried over TCP.
+ *
+ * It is really a FORMERR condition, but this is
+ * a workaround to accept replies from some
+ * implementations.
+ *
+ * Because the question section matching is not
+ * performed, the worst that could happen is
+ * that an attacker who gets past the ID and
+ * source port checks can force the use of
+ * TCP. This is considered an acceptable risk.
+ */
+ log_formerr(fctx,
+ "empty question section, "
+ "accepting it anyway as TC=1");
+ return (ISC_R_SUCCESS);
+ } else {
+ log_formerr(fctx, "empty question section");
+ return (DNS_R_FORMERR);
+ }
+ } else if (ISC_UNLIKELY(message->counts[DNS_SECTION_QUESTION] > 1)) {
+ log_formerr(fctx, "too many questions");
+ 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)) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char classbuf[DNS_RDATACLASS_FORMATSIZE];
+ char typebuf[DNS_RDATATYPE_FORMATSIZE];
+
+ dns_name_format(name, namebuf, sizeof(namebuf));
+ dns_rdataclass_format(rdataset->rdclass, classbuf,
+ sizeof(classbuf));
+ dns_rdatatype_format(rdataset->type, typebuf, sizeof(typebuf));
+ log_formerr(fctx, "question section mismatch: got %s/%s/%s",
+ namebuf, classbuf, typebuf);
+ 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 = 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).
+ *
+ * Requires:
+ * '*fctx' is shutting down.
+ *
+ * Returns:
+ * true if the resolver is exiting and this is the last fctx in the bucket.
+ */
+static bool
+maybe_destroy(fetchctx_t *fctx, bool locked) {
+ unsigned int bucketnum;
+ bool bucket_empty = false;
+ dns_resolver_t *res = fctx->res;
+ dns_validator_t *validator, *next_validator;
+ bool dodestroy = false;
+
+ REQUIRE(SHUTTINGDOWN(fctx));
+
+ bucketnum = fctx->bucketnum;
+ if (!locked)
+ LOCK(&res->buckets[bucketnum].lock);
+ if (fctx->pending != 0 || fctx->nqueries != 0)
+ goto unlock;
+
+ for (validator = ISC_LIST_HEAD(fctx->validators);
+ validator != NULL; validator = next_validator) {
+ next_validator = ISC_LIST_NEXT(validator, link);
+ dns_validator_cancel(validator);
+ }
+
+ if (fctx->references == 0 && ISC_LIST_EMPTY(fctx->validators)) {
+ bucket_empty = fctx_unlink(fctx);
+ dodestroy = true;
+ }
+ unlock:
+ if (!locked)
+ UNLOCK(&res->buckets[bucketnum].lock);
+ if (dodestroy)
+ fctx_destroy(fctx);
+ return (bucket_empty);
+}
+
+/*
+ * The validator has finished.
+ */
+static void
+validated(isc_task_t *task, isc_event_t *event) {
+ dns_adbaddrinfo_t *addrinfo;
+ dns_dbnode_t *node = NULL;
+ dns_dbnode_t *nsnode = NULL;
+ dns_fetchevent_t *hevent;
+ dns_name_t *name;
+ dns_rdataset_t *ardataset = NULL;
+ dns_rdataset_t *asigrdataset = NULL;
+ dns_rdataset_t *rdataset;
+ dns_rdataset_t *sigrdataset;
+ dns_resolver_t *res;
+ dns_valarg_t *valarg;
+ dns_validatorevent_t *vevent;
+ fetchctx_t *fctx;
+ bool chaining;
+ bool negative;
+ bool sentresponse;
+ isc_result_t eresult = ISC_R_SUCCESS;
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_stdtime_t now;
+ uint32_t ttl;
+ unsigned options;
+ uint32_t bucketnum;
+
+ UNUSED(task); /* for now */
+
+ REQUIRE(event->ev_type == DNS_EVENT_VALIDATORDONE);
+ valarg = event->ev_arg;
+ fctx = valarg->fctx;
+ res = fctx->res;
+ addrinfo = valarg->addrinfo;
+ REQUIRE(VALID_FCTX(fctx));
+ REQUIRE(!ISC_LIST_EMPTY(fctx->validators));
+
+ vevent = (dns_validatorevent_t *)event;
+ fctx->vresult = vevent->result;
+
+ FCTXTRACE("received validation completion event");
+
+ bucketnum = fctx->bucketnum;
+ LOCK(&res->buckets[bucketnum].lock);
+
+ 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->mctx, valarg, sizeof(*valarg));
+
+ negative = (vevent->rdataset == NULL);
+
+ sentresponse = (fctx->options & DNS_FETCHOPT_NOVALIDATE);
+
+ /*
+ * 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) {
+ bool bucket_empty;
+ bucket_empty = maybe_destroy(fctx, true);
+ UNLOCK(&res->buckets[bucketnum].lock);
+ if (bucket_empty)
+ empty_bucket(res);
+ goto cleanup_event;
+ }
+
+ isc_stdtime_get(&now);
+
+ /*
+ * 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 = true;
+ } else
+ chaining = 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(res, dns_resstatscounter_valfail);
+ fctx->valfail++;
+ fctx->vresult = vevent->result;
+ if (fctx->vresult != DNS_R_BROKENCHAIN) {
+ result = ISC_R_NOTFOUND;
+ if (vevent->rdataset != NULL)
+ result = dns_db_findnode(fctx->cache,
+ vevent->name,
+ 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);
+ }
+ if (fctx->vresult == DNS_R_BROKENCHAIN && !negative) {
+ /*
+ * Cache the data as pending for later validation.
+ */
+ result = ISC_R_NOTFOUND;
+ if (vevent->rdataset != NULL)
+ result = dns_db_findnode(fctx->cache,
+ vevent->name,
+ true, &node);
+ if (result == ISC_R_SUCCESS) {
+ (void)dns_db_addrdataset(fctx->cache, node,
+ NULL, now,
+ vevent->rdataset, 0,
+ NULL);
+ }
+ if (result == ISC_R_SUCCESS &&
+ vevent->sigrdataset != NULL)
+ (void)dns_db_addrdataset(fctx->cache, node,
+ NULL, now,
+ vevent->sigrdataset,
+ 0, NULL);
+ if (result == ISC_R_SUCCESS)
+ dns_db_detachnode(fctx->cache, &node);
+ }
+ result = fctx->vresult;
+ add_bad(fctx, addrinfo, result, badns_validation);
+ isc_event_free(&event);
+ UNLOCK(&res->buckets[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, __LINE__); /* Locks bucket. */
+ else if (result == DNS_R_BROKENCHAIN) {
+ isc_result_t tresult;
+ isc_time_t expire;
+ isc_interval_t i;
+
+ isc_interval_set(&i, DNS_RESOLVER_BADCACHETTL(fctx), 0);
+ tresult = isc_time_nowplusinterval(&expire, &i);
+ if (negative &&
+ (fctx->type == dns_rdatatype_dnskey ||
+ fctx->type == dns_rdatatype_dlv ||
+ fctx->type == dns_rdatatype_ds) &&
+ tresult == ISC_R_SUCCESS)
+ dns_resolver_addbadcache(res, &fctx->name,
+ fctx->type, &expire);
+ fctx_done(fctx, result, __LINE__); /* Locks bucket. */
+ } else
+ fctx_try(fctx, true, true); /* Locks bucket. */
+ return;
+ }
+
+
+ if (negative) {
+ dns_rdatatype_t covers;
+ FCTXTRACE("nonexistence validation OK");
+
+ inc_stats(res, dns_resstatscounter_valnegsuccess);
+
+ /*
+ * Cache DS NXDOMAIN seperately to other types.
+ */
+ if (fctx->rmessage->rcode == dns_rcode_nxdomain &&
+ fctx->type != dns_rdatatype_ds)
+ covers = dns_rdatatype_any;
+ else
+ covers = fctx->type;
+
+ result = dns_db_findnode(fctx->cache, vevent->name, 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 arbitrary zone.
+ */
+ ttl = res->view->maxncachettl;
+ if (fctx->type == dns_rdatatype_soa &&
+ covers == dns_rdatatype_any && res->zero_no_soa_ttl)
+ ttl = 0;
+
+ result = ncache_adderesult(fctx->rmessage, fctx->cache, node,
+ covers, now, ttl, vevent->optout,
+ vevent->secure, ardataset, &eresult);
+ if (result != ISC_R_SUCCESS)
+ goto noanswer_response;
+ goto answer_response;
+ } else
+ inc_stats(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);
+ }
+ } else if (vevent->rdataset->trust == dns_trust_answer &&
+ vevent->rdataset->type != dns_rdatatype_rrsig)
+ {
+ isc_result_t tresult;
+ dns_name_t *noqname = NULL;
+ tresult = findnoqname(fctx, vevent->name,
+ vevent->rdataset->type, &noqname);
+ if (tresult == ISC_R_SUCCESS && noqname != NULL) {
+ tresult = dns_rdataset_addnoqname(vevent->rdataset,
+ noqname);
+ RUNTIME_CHECK(tresult == 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, true, &node);
+ if (result != ISC_R_SUCCESS)
+ goto noanswer_response;
+
+ options = 0;
+ if ((fctx->options & DNS_FETCHOPT_PREFETCH) != 0)
+ options = DNS_DBADD_PREFETCH;
+ result = dns_db_addrdataset(fctx->cache, node, NULL, now,
+ vevent->rdataset, options, ardataset);
+ if (result != ISC_R_SUCCESS &&
+ result != DNS_R_UNCHANGED)
+ goto noanswer_response;
+ if (ardataset != NULL && NEGATIVE(ardataset)) {
+ 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, options,
+ asigrdataset);
+ if (result != ISC_R_SUCCESS &&
+ result != DNS_R_UNCHANGED)
+ goto noanswer_response;
+ }
+
+ if (sentresponse) {
+ bool bucket_empty = false;
+ /*
+ * If we only deferred the destroy because we wanted to cache
+ * the data, destroy now.
+ */
+ dns_db_detachnode(fctx->cache, &node);
+ if (SHUTTINGDOWN(fctx))
+ bucket_empty = maybe_destroy(fctx, true);
+ UNLOCK(&res->buckets[bucketnum].lock);
+ if (bucket_empty)
+ empty_bucket(res);
+ 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(&res->buckets[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, 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);
+ if (result != ISC_R_SUCCESS)
+ continue;
+ }
+ 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) {
+ /*
+ * Negative results must be indicated in event->result.
+ */
+ if (dns_rdataset_isassociated(hevent->rdataset) &&
+ NEGATIVE(hevent->rdataset)) {
+ INSIST(eresult == DNS_R_NCACHENXDOMAIN ||
+ eresult == DNS_R_NCACHENXRRSET);
+ }
+ 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(&res->buckets[bucketnum].lock);
+ fctx_done(fctx, result, __LINE__); /* Locks bucket. */
+
+ cleanup_event:
+ INSIST(node == NULL);
+ isc_event_free(&event);
+}
+
+static void
+fctx_log(void *arg, int level, const char *fmt, ...) {
+ char msgbuf[2048];
+ va_list args;
+ fetchctx_t *fctx = arg;
+
+ va_start(args, fmt);
+ vsnprintf(msgbuf, sizeof(msgbuf), fmt, args);
+ va_end(args);
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
+ DNS_LOGMODULE_RESOLVER, level,
+ "fctx %p(%s): %s", fctx, fctx->info, msgbuf);
+}
+
+static inline isc_result_t
+findnoqname(fetchctx_t *fctx, dns_name_t *name, dns_rdatatype_t type,
+ dns_name_t **noqnamep)
+{
+ dns_rdataset_t *nrdataset, *next, *sigrdataset;
+ dns_rdata_rrsig_t rrsig;
+ isc_result_t result;
+ unsigned int labels;
+ dns_section_t section;
+ dns_name_t *zonename;
+ dns_fixedname_t fzonename;
+ dns_name_t *closest;
+ dns_fixedname_t fclosest;
+ dns_name_t *nearest;
+ dns_fixedname_t fnearest;
+ dns_rdatatype_t found = dns_rdatatype_none;
+ dns_name_t *noqname = NULL;
+
+ FCTXTRACE("findnoqname");
+
+ REQUIRE(noqnamep != NULL && *noqnamep == NULL);
+
+ /*
+ * 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 == type)
+ break;
+ }
+
+ if (sigrdataset == NULL)
+ return (ISC_R_NOTFOUND);
+
+ labels = dns_name_countlabels(name);
+
+ for (result = dns_rdataset_first(sigrdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(sigrdataset)) {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdataset_current(sigrdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &rrsig, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ /* Wildcard has rrsig.labels < labels - 1. */
+ if (rrsig.labels + 1U >= labels)
+ continue;
+ break;
+ }
+
+ if (result == ISC_R_NOMORE)
+ return (ISC_R_NOTFOUND);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ zonename = dns_fixedname_initname(&fzonename);
+ closest = dns_fixedname_initname(&fclosest);
+ nearest = dns_fixedname_initname(&fnearest);
+
+#define NXND(x) ((x) == ISC_R_SUCCESS)
+
+ section = DNS_SECTION_AUTHORITY;
+ for (result = dns_message_firstname(fctx->rmessage, section);
+ result == ISC_R_SUCCESS;
+ result = dns_message_nextname(fctx->rmessage, section)) {
+ dns_name_t *nsec = NULL;
+ dns_message_currentname(fctx->rmessage, section, &nsec);
+ for (nrdataset = ISC_LIST_HEAD(nsec->list);
+ nrdataset != NULL; nrdataset = next) {
+ bool data = false, exists = false;
+ bool optout = false, unknown = false;
+ bool setclosest = false;
+ bool setnearest = false;
+
+ next = ISC_LIST_NEXT(nrdataset, link);
+ if (nrdataset->type != dns_rdatatype_nsec &&
+ nrdataset->type != dns_rdatatype_nsec3)
+ continue;
+
+ if (nrdataset->type == dns_rdatatype_nsec &&
+ NXND(dns_nsec_noexistnodata(type, name, nsec,
+ nrdataset, &exists,
+ &data, NULL, fctx_log,
+ fctx)))
+ {
+ if (!exists) {
+ noqname = nsec;
+ found = dns_rdatatype_nsec;
+ }
+ }
+
+ if (nrdataset->type == dns_rdatatype_nsec3 &&
+ NXND(dns_nsec3_noexistnodata(type, name, nsec,
+ nrdataset, zonename,
+ &exists, &data,
+ &optout, &unknown,
+ &setclosest,
+ &setnearest,
+ closest, nearest,
+ fctx_log, fctx)))
+ {
+ if (!exists && setnearest) {
+ noqname = nsec;
+ found = dns_rdatatype_nsec3;
+ }
+ }
+ }
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ if (noqname != NULL) {
+ for (sigrdataset = ISC_LIST_HEAD(noqname->list);
+ sigrdataset != NULL;
+ sigrdataset = ISC_LIST_NEXT(sigrdataset, link)) {
+ if (sigrdataset->type == dns_rdatatype_rrsig &&
+ sigrdataset->covers == found)
+ break;
+ }
+ if (sigrdataset != NULL)
+ *noqnamep = noqname;
+ }
+ return (result);
+}
+
+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 = NULL, *sigrdataset = NULL;
+ dns_rdataset_t *addedrdataset = NULL;
+ dns_rdataset_t *ardataset = NULL, *asigrdataset = NULL;
+ dns_rdataset_t *valrdataset = NULL, *valsigrdataset = NULL;
+ dns_dbnode_t *node = NULL, **anodep = NULL;
+ dns_db_t **adbp = NULL;
+ dns_name_t *aname = NULL;
+ dns_resolver_t *res = fctx->res;
+ bool need_validation = false;
+ bool secure_domain = false;
+ bool have_answer = false;
+ isc_result_t result, eresult = ISC_R_SUCCESS;
+ dns_fetchevent_t *event = NULL;
+ unsigned int options;
+ isc_task_t *task;
+ bool fail;
+ unsigned int valoptions = 0;
+ bool checknta = true;
+
+ /*
+ * The appropriate bucket lock must be held.
+ */
+ task = res->buckets[fctx->bucketnum].task;
+
+ /*
+ * Is DNSSEC validation required for this name?
+ */
+ if ((fctx->options & DNS_FETCHOPT_NONTA) != 0) {
+ valoptions |= DNS_VALIDATOR_NONTA;
+ checknta = false;
+ }
+
+ if (res->view->enablevalidation) {
+ result = issecuredomain(res->view, name, fctx->type,
+ now, checknta, &secure_domain);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ if (!secure_domain && res->view->dlv != NULL) {
+ valoptions |= DNS_VALIDATOR_DLV;
+ secure_domain = true;
+ }
+ }
+
+ if ((fctx->options & DNS_FETCHOPT_NOCDFLAG) != 0) {
+ valoptions |= DNS_VALIDATOR_NOCDFLAG;
+ }
+
+ if ((fctx->options & DNS_FETCHOPT_NOVALIDATE) != 0) {
+ need_validation = false;
+ } else {
+ need_validation = secure_domain;
+ }
+
+ if (((name->attributes & DNS_NAMEATTR_ANSWER) != 0) &&
+ (!need_validation))
+ {
+ have_answer = 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, true, &node);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ /*
+ * Cache or validate each cacheable rdataset.
+ */
+ fail = (fctx->res->options & DNS_RESOLVER_CHECKNAMESFAIL);
+ 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;
+ }
+
+ /*
+ * Mark the rdataset as being prefetch eligible.
+ */
+ if (rdataset->ttl > fctx->res->view->prefetch_eligible) {
+ rdataset->attributes |= DNS_RDATASETATTR_PREFETCH;
+ }
+
+ /*
+ * 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 this RRset is in a secure domain, is in bailiwick,
+ * and is not glue, attempt DNSSEC validation. (We do not
+ * attempt to validate glue or out-of-bailiwick data--even
+ * though there might be some performance benefit to doing
+ * so--because it makes it simpler and safer to ensure that
+ * records from a secure domain are only cached if validated
+ * within the context of a query to the domain that owns
+ * them.)
+ */
+ if (secure_domain && rdataset->trust != dns_trust_glue &&
+ !EXTERNAL(rdataset))
+ {
+ dns_trust_t trust;
+
+ /*
+ * RRSIGs are validated as part of validating the
+ * type they cover.
+ */
+ if (rdataset->type == dns_rdatatype_rrsig) {
+ continue;
+ }
+
+ if (sigrdataset == NULL && need_validation &&
+ !ANSWER(rdataset))
+ {
+ /*
+ * Ignore unrelated 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;
+ }
+
+ /*
+ * Mark the rdataset as being prefetch eligible.
+ */
+ if (rdataset->ttl > fctx->res->view->prefetch_eligible)
+ {
+ rdataset->attributes |=
+ DNS_RDATASETATTR_PREFETCH;
+ }
+
+ /*
+ * Cache this rdataset/sigrdataset pair as
+ * pending data. Track whether it was additional
+ * or not. If this was a priming query, additional
+ * should be cached as glue.
+ */
+ if (rdataset->trust == dns_trust_additional) {
+ trust = dns_trust_pending_additional;
+ } else {
+ trust = dns_trust_pending_answer;
+ }
+
+ rdataset->trust = trust;
+ if (sigrdataset != NULL) {
+ sigrdataset->trust = trust;
+ }
+ if (!need_validation || !ANSWER(rdataset)) {
+ options = 0;
+ if (ANSWER(rdataset) &&
+ rdataset->type != dns_rdatatype_rrsig)
+ {
+ isc_result_t tresult;
+ dns_name_t *noqname = NULL;
+ tresult = findnoqname(fctx, name,
+ rdataset->type,
+ &noqname);
+ if (tresult == ISC_R_SUCCESS &&
+ noqname != NULL)
+ {
+ (void) dns_rdataset_addnoqname(
+ rdataset, noqname);
+ }
+ }
+ if ((fctx->options &
+ DNS_FETCHOPT_PREFETCH) != 0)
+ {
+ options = DNS_DBADD_PREFETCH;
+ }
+ if ((fctx->options &
+ DNS_FETCHOPT_NOCACHED) != 0)
+ {
+ options |= DNS_DBADD_FORCE;
+ }
+ addedrdataset = ardataset;
+ result = dns_db_addrdataset(fctx->cache, node,
+ NULL, now, rdataset,
+ options,
+ addedrdataset);
+ if (result == DNS_R_UNCHANGED) {
+ result = ISC_R_SUCCESS;
+ if (!need_validation &&
+ ardataset != NULL &&
+ NEGATIVE(ardataset))
+ {
+ /*
+ * 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) {
+ addedrdataset = asigrdataset;
+ result = dns_db_addrdataset(fctx->cache,
+ node, NULL, now,
+ sigrdataset,
+ options,
+ 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);
+ }
+ } 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 if ((fctx->options & DNS_FETCHOPT_PREFETCH) != 0)
+ {
+ options = DNS_DBADD_PREFETCH;
+ } else {
+ options = 0;
+ }
+
+ if (ANSWER(rdataset) &&
+ rdataset->type != dns_rdatatype_rrsig)
+ {
+ isc_result_t tresult;
+ dns_name_t *noqname = NULL;
+ tresult = findnoqname(fctx, name,
+ rdataset->type, &noqname);
+ if (tresult == ISC_R_SUCCESS &&
+ noqname != NULL)
+ {
+ (void) dns_rdataset_addnoqname(
+ rdataset, noqname);
+ }
+ }
+
+ /*
+ * 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 &&
+ NEGATIVE(ardataset))
+ {
+ /*
+ * 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) {
+ dns_rdatatype_t vtype = fctx->type;
+ if (CHAINING(valrdataset)) {
+ if (valrdataset->type == dns_rdatatype_cname) {
+ vtype = dns_rdatatype_cname;
+ } else {
+ vtype = dns_rdatatype_dname;
+ }
+ }
+ result = valcreate(fctx, addrinfo, name, vtype, 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) &&
+ NEGATIVE(event->rdataset))
+ {
+ INSIST(eresult == DNS_R_NCACHENXDOMAIN ||
+ eresult == DNS_R_NCACHENXRRSET);
+ }
+ event->result = eresult;
+ if (adbp != NULL && *adbp != NULL) {
+ if (anodep != NULL && *anodep != NULL) {
+ dns_db_detachnode(*adbp, anodep);
+ }
+ dns_db_detach(adbp);
+ }
+ 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,
+ bool optout, bool secure,
+ 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;
+ }
+ if (secure)
+ result = dns_ncache_addoptout(message, cache, node, covers,
+ now, maxttl, optout, ardataset);
+ else
+ result = dns_ncache_add(message, cache, node, covers, now,
+ maxttl, 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 (NEGATIVE(ardataset)) {
+ /*
+ * 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;
+ bool need_validation, secure_domain;
+ dns_name_t *aname;
+ dns_fetchevent_t *event;
+ uint32_t ttl;
+ unsigned int valoptions = 0;
+ bool checknta = true;
+
+ FCTXTRACE("ncache_message");
+
+ fctx->attributes &= ~FCTX_ATTR_WANTNCACHE;
+
+ res = fctx->res;
+ need_validation = false;
+ POST(need_validation);
+ secure_domain = 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->options & DNS_FETCHOPT_NONTA) != 0) {
+ valoptions |= DNS_VALIDATOR_NONTA;
+ checknta = false;
+ }
+
+ if (fctx->res->view->enablevalidation) {
+ result = issecuredomain(res->view, name, fctx->type,
+ now, checknta, &secure_domain);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ if (!secure_domain && res->view->dlv != NULL) {
+ valoptions |= DNS_VALIDATOR_DLV;
+ secure_domain = true;
+ }
+ }
+
+ if ((fctx->options & DNS_FETCHOPT_NOCDFLAG) != 0)
+ valoptions |= DNS_VALIDATOR_NOCDFLAG;
+
+ if ((fctx->options & DNS_FETCHOPT_NOVALIDATE) != 0)
+ need_validation = 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_answer;
+ 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, 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 arbitrary 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, false,
+ 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;
+ if (adbp != NULL && *adbp != NULL) {
+ if (anodep != NULL && *anodep != NULL)
+ dns_db_detachnode(*adbp, anodep);
+ dns_db_detach(adbp);
+ }
+ 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,
+ bool external, bool 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_section(void *arg, dns_name_t *addname, dns_rdatatype_t type,
+ dns_section_t section)
+{
+ fetchctx_t *fctx = arg;
+ isc_result_t result;
+ dns_name_t *name = NULL;
+ dns_rdataset_t *rdataset = NULL;
+ bool external;
+ dns_rdatatype_t rtype;
+ bool gluing;
+
+ REQUIRE(VALID_FCTX(fctx));
+
+#if CHECK_FOR_GLUE_IN_ANSWER
+ if (section == DNS_SECTION_ANSWER && type != dns_rdatatype_a)
+ return (ISC_R_SUCCESS);
+#endif
+
+ gluing = (GLUING(fctx) ||
+ (fctx->type == dns_rdatatype_ns &&
+ dns_name_equal(&fctx->name, dns_rootname)));
+
+ result = dns_message_findname(fctx->rmessage, section, addname,
+ dns_rdatatype_any, 0, &name, NULL);
+ if (result == ISC_R_SUCCESS) {
+ external = !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 isc_result_t
+check_related(void *arg, dns_name_t *addname, dns_rdatatype_t type) {
+ return (check_section(arg, addname, type, DNS_SECTION_ADDITIONAL));
+}
+
+#ifndef CHECK_FOR_GLUE_IN_ANSWER
+#define CHECK_FOR_GLUE_IN_ANSWER 0
+#endif
+#if CHECK_FOR_GLUE_IN_ANSWER
+static isc_result_t
+check_answer(void *arg, dns_name_t *addname, dns_rdatatype_t type) {
+ return (check_section(arg, addname, type, DNS_SECTION_ANSWER));
+}
+#endif
+
+static void
+chase_additional(fetchctx_t *fctx) {
+ bool rescan;
+ dns_section_t section = DNS_SECTION_ADDITIONAL;
+ isc_result_t result;
+
+ again:
+ rescan = 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 = true;
+ }
+ }
+ }
+ if (rescan)
+ goto again;
+}
+
+static bool
+is_answeraddress_allowed(dns_view_t *view, dns_name_t *name,
+ dns_rdataset_t *rdataset)
+{
+ isc_result_t result;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ struct in_addr ina;
+ struct in6_addr in6a;
+ isc_netaddr_t netaddr;
+ char addrbuf[ISC_NETADDR_FORMATSIZE];
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char classbuf[64];
+ char typebuf[64];
+ int match;
+
+ /* By default, we allow any addresses. */
+ if (view->denyansweracl == NULL)
+ return (true);
+
+ /*
+ * If the owner name matches one in the exclusion list, either exactly
+ * or partially, allow it.
+ */
+ if (view->answeracl_exclude != NULL) {
+ dns_rbtnode_t *node = NULL;
+
+ result = dns_rbt_findnode(view->answeracl_exclude, name, NULL,
+ &node, NULL, 0, NULL, NULL);
+
+ if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH)
+ return (true);
+ }
+
+ /*
+ * Otherwise, search the filter list for a match for each address
+ * record. If a match is found, the address should be filtered,
+ * so should the entire answer.
+ */
+ for (result = dns_rdataset_first(rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(rdataset)) {
+ dns_rdata_reset(&rdata);
+ dns_rdataset_current(rdataset, &rdata);
+ if (rdataset->type == dns_rdatatype_a) {
+ INSIST(rdata.length == sizeof(ina.s_addr));
+ memmove(&ina.s_addr, rdata.data, sizeof(ina.s_addr));
+ isc_netaddr_fromin(&netaddr, &ina);
+ } else {
+ INSIST(rdata.length == sizeof(in6a.s6_addr));
+ memmove(in6a.s6_addr, rdata.data, sizeof(in6a.s6_addr));
+ isc_netaddr_fromin6(&netaddr, &in6a);
+ }
+
+ result = dns_acl_match(&netaddr, NULL, view->denyansweracl,
+ &view->aclenv, &match, NULL);
+
+ if (result == ISC_R_SUCCESS && match > 0) {
+ isc_netaddr_format(&netaddr, addrbuf, sizeof(addrbuf));
+ 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,
+ "answer address %s denied for %s/%s/%s",
+ addrbuf, namebuf, typebuf, classbuf);
+ return (false);
+ }
+ }
+
+ return (true);
+}
+
+static bool
+is_answertarget_allowed(fetchctx_t *fctx, dns_name_t *qname, dns_name_t *rname,
+ dns_rdataset_t *rdataset, bool *chainingp)
+{
+ isc_result_t result;
+ dns_rbtnode_t *node = NULL;
+ char qnamebuf[DNS_NAME_FORMATSIZE];
+ char tnamebuf[DNS_NAME_FORMATSIZE];
+ char classbuf[64];
+ char typebuf[64];
+ dns_name_t *tname = NULL;
+ dns_rdata_cname_t cname;
+ dns_rdata_dname_t dname;
+ dns_view_t *view = fctx->res->view;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ unsigned int nlabels;
+ dns_fixedname_t fixed;
+ dns_name_t prefix;
+ int order;
+
+ REQUIRE(rdataset != NULL);
+ REQUIRE(rdataset->type == dns_rdatatype_cname ||
+ rdataset->type == dns_rdatatype_dname);
+
+ /*
+ * By default, we allow any target name.
+ * If newqname != NULL we also need to extract the newqname.
+ */
+ if (chainingp == NULL && view->denyanswernames == NULL)
+ return (true);
+
+ result = dns_rdataset_first(rdataset);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ dns_rdataset_current(rdataset, &rdata);
+ switch (rdataset->type) {
+ case dns_rdatatype_cname:
+ result = dns_rdata_tostruct(&rdata, &cname, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ tname = &cname.cname;
+ break;
+ case dns_rdatatype_dname:
+ if (dns_name_fullcompare(qname, rname, &order, &nlabels) !=
+ dns_namereln_subdomain)
+ {
+ return (true);
+ }
+ result = dns_rdata_tostruct(&rdata, &dname, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ dns_name_init(&prefix, NULL);
+ tname = dns_fixedname_initname(&fixed);
+ nlabels = dns_name_countlabels(rname);
+ dns_name_split(qname, nlabels, &prefix, NULL);
+ result = dns_name_concatenate(&prefix, &dname.dname, tname,
+ NULL);
+ if (result == DNS_R_NAMETOOLONG) {
+ if (chainingp != NULL) {
+ *chainingp = true;
+ }
+ return (true);
+ }
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ break;
+ default:
+ INSIST(0);
+ }
+
+ if (chainingp != NULL)
+ *chainingp = true;
+
+ if (view->denyanswernames == NULL)
+ return (true);
+
+ /*
+ * If the owner name matches one in the exclusion list, either exactly
+ * or partially, allow it.
+ */
+ if (view->answernames_exclude != NULL) {
+ result = dns_rbt_findnode(view->answernames_exclude, qname,
+ NULL, &node, NULL, 0, NULL, NULL);
+ if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH)
+ return (true);
+ }
+
+ /*
+ * If the target name is a subdomain of the search domain, allow it.
+ */
+ if (dns_name_issubdomain(tname, &fctx->domain))
+ return (true);
+
+ /*
+ * Otherwise, apply filters.
+ */
+ result = dns_rbt_findnode(view->denyanswernames, tname, NULL, &node,
+ NULL, 0, NULL, NULL);
+ if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
+ dns_name_format(qname, qnamebuf, sizeof(qnamebuf));
+ dns_name_format(tname, tnamebuf, sizeof(tnamebuf));
+ dns_rdatatype_format(rdataset->type, typebuf, sizeof(typebuf));
+ dns_rdataclass_format(view->rdclass, classbuf,
+ sizeof(classbuf));
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
+ DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE,
+ "%s target %s denied for %s/%s",
+ typebuf, tnamebuf, qnamebuf, classbuf);
+ return (false);
+ }
+
+ return (true);
+}
+
+static void
+trim_ns_ttl(fetchctx_t *fctx, dns_name_t *name, dns_rdataset_t *rdataset) {
+ char ns_namebuf[DNS_NAME_FORMATSIZE];
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char tbuf[DNS_RDATATYPE_FORMATSIZE];
+
+ if (fctx->ns_ttl_ok && rdataset->ttl > fctx->ns_ttl) {
+ dns_name_format(name, ns_namebuf, sizeof(ns_namebuf));
+ dns_name_format(&fctx->name, namebuf, sizeof(namebuf));
+ dns_rdatatype_format(fctx->type, tbuf, sizeof(tbuf));
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
+ DNS_LOGMODULE_RESOLVER, ISC_LOG_DEBUG(10),
+ "fctx %p: trimming ttl of %s/NS for %s/%s: "
+ "%u -> %u", fctx, ns_namebuf, namebuf, tbuf,
+ rdataset->ttl, fctx->ns_ttl);
+ rdataset->ttl = fctx->ns_ttl;
+ }
+}
+
+/*
+ * Handle a no-answer response (NXDOMAIN, NXRRSET, or referral).
+ * If look_in_options has LOOK_FOR_NS_IN_ANSWER then we look in the answer
+ * section for the NS RRset if the query type is NS; if it has
+ * LOOK_FOR_GLUE_IN_ANSWER we look for glue incorrectly returned in the answer
+ * section for A and AAAA queries.
+ */
+#define LOOK_FOR_NS_IN_ANSWER 0x1
+#define LOOK_FOR_GLUE_IN_ANSWER 0x2
+
+static isc_result_t
+noanswer_response(fetchctx_t *fctx, dns_name_t *oqname,
+ unsigned int look_in_options)
+{
+ isc_result_t result;
+ dns_message_t *message;
+ dns_name_t *name, *qname, *ns_name, *soa_name, *ds_name, *save_name;
+ dns_rdataset_t *rdataset, *ns_rdataset;
+ bool aa, negative_response;
+ dns_rdatatype_t type, save_type;
+ dns_section_t section;
+
+ FCTXTRACE("noanswer_response");
+
+ if ((look_in_options & LOOK_FOR_NS_IN_ANSWER) != 0) {
+ INSIST(fctx->type == dns_rdatatype_ns);
+ section = DNS_SECTION_ANSWER;
+ } else
+ section = DNS_SECTION_AUTHORITY;
+
+ 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 = true;
+ else
+ aa = false;
+ qname = &fctx->name;
+ } else {
+ /*
+ * We're being invoked by answer_response() after it has
+ * followed a CNAME/DNAME chain.
+ */
+ qname = oqname;
+ aa = 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 = false;
+ if (message->rcode == dns_rcode_nxdomain ||
+ (message->counts[DNS_SECTION_ANSWER] == 0 &&
+ message->counts[DNS_SECTION_AUTHORITY] == 0))
+ negative_response = true;
+
+ /*
+ * Process the authority section.
+ */
+ ns_name = NULL;
+ ns_rdataset = NULL;
+ soa_name = NULL;
+ ds_name = NULL;
+ save_name = NULL;
+ save_type = dns_rdatatype_none;
+ 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))) {
+ char qbuf[DNS_NAME_FORMATSIZE];
+ char nbuf[DNS_NAME_FORMATSIZE];
+ char tbuf[DNS_RDATATYPE_FORMATSIZE];
+ dns_rdatatype_format(type, tbuf,
+ sizeof(tbuf));
+ dns_name_format(name, nbuf,
+ sizeof(nbuf));
+ dns_name_format(qname, qbuf,
+ sizeof(qbuf));
+ log_formerr(fctx,
+ "unrelated %s %s in "
+ "%s authority section",
+ tbuf, nbuf, qbuf);
+ goto nextname;
+ }
+ 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) {
+ log_formerr(fctx,
+ "multiple NS "
+ "RRsets in "
+ "authority "
+ "section");
+ 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) {
+ log_formerr(fctx,
+ "multiple SOA "
+ "RRs in "
+ "authority "
+ "section");
+ 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 if (ISFORWARDER(fctx->addrinfo))
+ rdataset->trust =
+ dns_trust_answer;
+ else
+ rdataset->trust =
+ dns_trust_additional;
+ }
+ }
+ }
+ nextname:
+ result = dns_message_nextname(message, section);
+ if (result == ISC_R_NOMORE)
+ break;
+ else if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+
+ log_ns_ttl(fctx, "noanswer_response");
+
+ if (ns_rdataset != NULL && dns_name_equal(&fctx->domain, ns_name) &&
+ !dns_name_equal(ns_name, dns_rootname))
+ trim_ns_ttl(fctx, ns_name, ns_rdataset);
+
+ /*
+ * 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 = 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 if (ISFORWARDER(fctx->addrinfo))
+ rdataset->trust =
+ dns_trust_answer;
+ 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 RRset.
+ */
+ if (ns_name == NULL) {
+ log_formerr(fctx,
+ "DS with no "
+ "referral");
+ return (DNS_R_FORMERR);
+ }
+ if (rdataset->type ==
+ dns_rdatatype_ds) {
+ if (ds_name != NULL &&
+ name != ds_name) {
+ log_formerr(fctx,
+ "DS doesn't "
+ "match "
+ "referral "
+ "(NS)");
+ 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 if (ISFORWARDER(fctx->addrinfo))
+ rdataset->trust =
+ dns_trust_answer;
+ else
+ rdataset->trust =
+ dns_trust_additional;
+ }
+ }
+ } else {
+ save_name = name;
+ save_type = ISC_LIST_HEAD(name->list)->type;
+ }
+ 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.
+ */
+ if (save_name == NULL) {
+ log_formerr(fctx, "invalid response");
+ return (DNS_R_FORMERR);
+ }
+ if (!dns_name_issubdomain(save_name, &fctx->domain)) {
+ char nbuf[DNS_NAME_FORMATSIZE];
+ char dbuf[DNS_NAME_FORMATSIZE];
+ char tbuf[DNS_RDATATYPE_FORMATSIZE];
+
+ dns_rdatatype_format(save_type, tbuf,
+ sizeof(tbuf));
+ dns_name_format(save_name, nbuf, sizeof(nbuf));
+ dns_name_format(&fctx->domain, dbuf,
+ sizeof(dbuf));
+
+ log_formerr(fctx, "Name %s (%s) not subdomain"
+ " of zone %s -- invalid response",
+ nbuf, tbuf, dbuf);
+ } else {
+ log_formerr(fctx, "invalid response");
+ }
+ 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) {
+ log_formerr(fctx, "NS/SOA mismatch");
+ 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)) {
+ log_formerr(fctx, "non-improving referral");
+ 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)) {
+ /* Logged twice */
+ log_formerr(fctx, "referral to non-parent");
+ 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);
+#if CHECK_FOR_GLUE_IN_ANSWER
+ /*
+ * Look in the answer section for "glue" that is incorrectly
+ * returned as a answer. This is needed if the server also
+ * minimizes the response size by not adding records to the
+ * additional section that are in the answer section or if
+ * the record gets dropped due to message size constraints.
+ */
+ if ((look_in_options & LOOK_FOR_GLUE_IN_ANSWER) != 0 &&
+ (fctx->type == dns_rdatatype_aaaa ||
+ fctx->type == dns_rdatatype_a))
+ (void)dns_rdataset_additionaldata(ns_rdataset,
+ check_answer, fctx);
+#endif
+ 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);
+ fcount_decr(fctx);
+ dns_name_free(&fctx->domain, fctx->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->mctx, &fctx->domain);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ result = fcount_incr(fctx, true);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ fctx->attributes |= FCTX_ATTR_WANTCACHE;
+ fctx->ns_ttl_ok = false;
+ log_ns_ttl(fctx, "DELEGATION");
+ 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 bool
+validinanswer(dns_rdataset_t *rdataset, fetchctx_t *fctx) {
+ if (rdataset->type == dns_rdatatype_nsec3) {
+ /*
+ * NSEC3 records are not allowed to
+ * appear in the answer section.
+ */
+ log_formerr(fctx, "NSEC3 in answer");
+ return (false);
+ }
+ if (rdataset->type == dns_rdatatype_tkey) {
+ /*
+ * TKEY is not a valid record in a
+ * response to any query we can make.
+ */
+ log_formerr(fctx, "TKEY in answer");
+ return (false);
+ }
+ if (rdataset->rdclass != fctx->res->rdclass) {
+ log_formerr(fctx, "Mismatched class in answer");
+ return (false);
+ }
+ return (true);
+}
+
+static isc_result_t
+answer_response(fetchctx_t *fctx) {
+ isc_result_t result;
+ dns_message_t *message = NULL;
+ dns_name_t *name = NULL, *qname = NULL, *ns_name = NULL;
+ dns_name_t *aname = NULL, *cname = NULL, *dname = NULL;
+ dns_rdataset_t *rdataset = NULL, *sigrdataset = NULL;
+ dns_rdataset_t *ardataset = NULL, *crdataset = NULL;
+ dns_rdataset_t *drdataset = NULL, *ns_rdataset = NULL;
+ bool done = false, aa;
+ unsigned int dname_labels, domain_labels;
+ bool chaining = false;
+ dns_rdatatype_t type;
+ dns_view_t *view = NULL;
+ dns_trust_t trust;
+
+ REQUIRE(VALID_FCTX(fctx));
+
+ FCTXTRACE("answer_response");
+
+ message = fctx->rmessage;
+ qname = &fctx->name;
+ view = fctx->res->view;
+ type = fctx->type;
+
+ /*
+ * There can be multiple RRSIG and SIG records at a name so
+ * we treat these types as a subset of ANY.
+ */
+ if (type == dns_rdatatype_rrsig || type == dns_rdatatype_sig) {
+ type = dns_rdatatype_any;
+ }
+
+ /*
+ * Bigger than any valid DNAME label count.
+ */
+ dname_labels = dns_name_countlabels(qname);
+ domain_labels = dns_name_countlabels(&fctx->domain);
+
+ /*
+ * Perform a single pass looking for the answer, cname or covering
+ * dname.
+ */
+ for (result = dns_message_firstname(message, DNS_SECTION_ANSWER);
+ result == ISC_R_SUCCESS;
+ result = dns_message_nextname(message, DNS_SECTION_ANSWER))
+ {
+ int order;
+ unsigned int nlabels;
+ dns_namereln_t namereln;
+
+ name = NULL;
+ dns_message_currentname(message, DNS_SECTION_ANSWER, &name);
+ namereln = dns_name_fullcompare(qname, name, &order, &nlabels);
+ switch (namereln) {
+ case dns_namereln_equal:
+ for (rdataset = ISC_LIST_HEAD(name->list);
+ rdataset != NULL;
+ rdataset = ISC_LIST_NEXT(rdataset, link))
+ {
+ if (rdataset->type == type ||
+ type == dns_rdatatype_any)
+ {
+ aname = name;
+ if (type != dns_rdatatype_any) {
+ ardataset = rdataset;
+ }
+ break;
+ }
+ if (rdataset->type == dns_rdatatype_cname) {
+ cname = name;
+ crdataset = rdataset;
+ break;
+ }
+ }
+ break;
+
+ case dns_namereln_subdomain:
+ /*
+ * In-scope DNAME records must have at least
+ * as many labels as the domain being queried.
+ * They also must be less that qname's labels
+ * and any previously found dname.
+ */
+ if (nlabels >= dname_labels || nlabels < domain_labels)
+ {
+ continue;
+ }
+
+ /*
+ * We are looking for the shortest DNAME if there
+ * are multiple ones (which there shouldn't be).
+ */
+ for (rdataset = ISC_LIST_HEAD(name->list);
+ rdataset != NULL;
+ rdataset = ISC_LIST_NEXT(rdataset, link))
+ {
+ if (rdataset->type != dns_rdatatype_dname) {
+ continue;
+ }
+ dname = name;
+ drdataset = rdataset;
+ dname_labels = nlabels;
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (dname != NULL) {
+ aname = NULL;
+ ardataset = NULL;
+ cname = NULL;
+ crdataset = NULL;
+ } else if (aname != NULL) {
+ cname = NULL;
+ crdataset = NULL;
+ }
+
+ aa = (message->flags & DNS_MESSAGEFLAG_AA);
+ trust = aa ? dns_trust_authanswer : dns_trust_answer;
+
+ if (aname != NULL && type == dns_rdatatype_any) {
+ for (rdataset = ISC_LIST_HEAD(aname->list);
+ rdataset != NULL;
+ rdataset = ISC_LIST_NEXT(rdataset, link))
+ {
+ if (!validinanswer(rdataset, fctx)) {
+ return (DNS_R_FORMERR);
+ }
+ if ((fctx->type == dns_rdatatype_sig ||
+ fctx->type == dns_rdatatype_rrsig) &&
+ rdataset->type != fctx->type)
+ {
+ continue;
+ }
+ if ((rdataset->type == dns_rdatatype_a ||
+ rdataset->type == dns_rdatatype_aaaa) &&
+ !is_answeraddress_allowed(view, aname, rdataset))
+ {
+ return (DNS_R_SERVFAIL);
+ }
+ if ((rdataset->type == dns_rdatatype_cname ||
+ rdataset->type == dns_rdatatype_dname) &&
+ !is_answertarget_allowed(fctx, qname, aname,
+ rdataset, NULL))
+ {
+ return (DNS_R_SERVFAIL);
+ }
+ aname->attributes |= DNS_NAMEATTR_CACHE;
+ aname->attributes |= DNS_NAMEATTR_ANSWER;
+ rdataset->attributes |= DNS_RDATASETATTR_ANSWER;
+ rdataset->attributes |= DNS_RDATASETATTR_CACHE;
+ rdataset->trust = trust;
+ (void)dns_rdataset_additionaldata(rdataset,
+ check_related,
+ fctx);
+ }
+ } else if (aname != NULL) {
+ if (!validinanswer(ardataset, fctx))
+ return (DNS_R_FORMERR);
+ if ((ardataset->type == dns_rdatatype_a ||
+ ardataset->type == dns_rdatatype_aaaa) &&
+ !is_answeraddress_allowed(view, aname, ardataset)) {
+ return (DNS_R_SERVFAIL);
+ }
+ if ((ardataset->type == dns_rdatatype_cname ||
+ ardataset->type == dns_rdatatype_dname) &&
+ type != ardataset->type &&
+ type != dns_rdatatype_any &&
+ !is_answertarget_allowed(fctx, qname, aname, ardataset,
+ NULL))
+ {
+ return (DNS_R_SERVFAIL);
+ }
+ aname->attributes |= DNS_NAMEATTR_CACHE;
+ aname->attributes |= DNS_NAMEATTR_ANSWER;
+ ardataset->attributes |= DNS_RDATASETATTR_ANSWER;
+ ardataset->attributes |= DNS_RDATASETATTR_CACHE;
+ ardataset->trust = trust;
+ (void)dns_rdataset_additionaldata(ardataset, check_related,
+ fctx);
+ for (sigrdataset = ISC_LIST_HEAD(aname->list);
+ sigrdataset != NULL;
+ sigrdataset = ISC_LIST_NEXT(sigrdataset, link)) {
+ if (!validinanswer(sigrdataset, fctx))
+ return (DNS_R_FORMERR);
+ if (sigrdataset->type != dns_rdatatype_rrsig ||
+ sigrdataset->covers != type)
+ continue;
+ sigrdataset->attributes |= DNS_RDATASETATTR_ANSWERSIG;
+ sigrdataset->attributes |= DNS_RDATASETATTR_CACHE;
+ sigrdataset->trust = trust;
+ break;
+ }
+ } else if (cname != NULL) {
+ if (!validinanswer(crdataset, fctx)) {
+ return (DNS_R_FORMERR);
+ }
+ if (type == dns_rdatatype_rrsig || type == dns_rdatatype_key ||
+ type == dns_rdatatype_nsec)
+ {
+ char buf[DNS_RDATATYPE_FORMATSIZE];
+ dns_rdatatype_format(type, buf, sizeof(buf));
+ log_formerr(fctx, "CNAME response for %s RR", buf);
+ return (DNS_R_FORMERR);
+ }
+ if (!is_answertarget_allowed(fctx, qname, cname, crdataset,
+ NULL))
+ {
+ return (DNS_R_SERVFAIL);
+ }
+ cname->attributes |= DNS_NAMEATTR_CACHE;
+ cname->attributes |= DNS_NAMEATTR_ANSWER;
+ cname->attributes |= DNS_NAMEATTR_CHAINING;
+ crdataset->attributes |= DNS_RDATASETATTR_ANSWER;
+ crdataset->attributes |= DNS_RDATASETATTR_CACHE;
+ crdataset->attributes |= DNS_RDATASETATTR_CHAINING;
+ crdataset->trust = trust;
+ for (sigrdataset = ISC_LIST_HEAD(cname->list);
+ sigrdataset != NULL;
+ sigrdataset = ISC_LIST_NEXT(sigrdataset, link))
+ {
+ if (!validinanswer(sigrdataset, fctx)) {
+ return (DNS_R_FORMERR);
+ }
+ if (sigrdataset->type != dns_rdatatype_rrsig ||
+ sigrdataset->covers != dns_rdatatype_cname)
+ {
+ continue;
+ }
+ sigrdataset->attributes |= DNS_RDATASETATTR_ANSWERSIG;
+ sigrdataset->attributes |= DNS_RDATASETATTR_CACHE;
+ sigrdataset->trust = trust;
+ break;
+ }
+ chaining = true;
+ } else if (dname != NULL) {
+ if (!validinanswer(drdataset, fctx)) {
+ return (DNS_R_FORMERR);
+ }
+ if (!is_answertarget_allowed(fctx, qname, dname, drdataset,
+ &chaining)) {
+ return (DNS_R_SERVFAIL);
+ }
+ dname->attributes |= DNS_NAMEATTR_CACHE;
+ dname->attributes |= DNS_NAMEATTR_ANSWER;
+ dname->attributes |= DNS_NAMEATTR_CHAINING;
+ drdataset->attributes |= DNS_RDATASETATTR_ANSWER;
+ drdataset->attributes |= DNS_RDATASETATTR_CACHE;
+ drdataset->attributes |= DNS_RDATASETATTR_CHAINING;
+ drdataset->trust = trust;
+ for (sigrdataset = ISC_LIST_HEAD(dname->list);
+ sigrdataset != NULL;
+ sigrdataset = ISC_LIST_NEXT(sigrdataset, link))
+ {
+ if (!validinanswer(sigrdataset, fctx)) {
+ return (DNS_R_FORMERR);
+ }
+ if (sigrdataset->type != dns_rdatatype_rrsig ||
+ sigrdataset->covers != dns_rdatatype_dname)
+ {
+ continue;
+ }
+ sigrdataset->attributes |= DNS_RDATASETATTR_ANSWERSIG;
+ sigrdataset->attributes |= DNS_RDATASETATTR_CACHE;
+ sigrdataset->trust = trust;
+ break;
+ }
+ } else {
+ log_formerr(fctx, "reply has no 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) {
+ return (ISC_R_SUCCESS);
+ }
+
+ /*
+ * We didn't end with an incomplete chain, so the rcode should be
+ * "no error".
+ */
+ if (message->rcode != dns_rcode_noerror) {
+ log_formerr(fctx, "CNAME/DNAME chain complete, but RCODE "
+ "indicates error");
+ 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.
+ */
+ result = dns_message_firstname(message, DNS_SECTION_AUTHORITY);
+ while (!done && result == ISC_R_SUCCESS) {
+ bool external;
+ name = NULL;
+ dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name);
+ external = !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;
+ }
+
+ if (rdataset->type == dns_rdatatype_ns)
+ {
+ ns_name = name;
+ ns_rdataset = rdataset;
+ }
+ /*
+ * Mark any additional data related
+ * to this rdataset.
+ */
+ (void)dns_rdataset_additionaldata(
+ rdataset,
+ check_related,
+ fctx);
+ done = true;
+ }
+ }
+ }
+ result = dns_message_nextname(message, DNS_SECTION_AUTHORITY);
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+
+ log_ns_ttl(fctx, "answer_response");
+
+ if (ns_rdataset != NULL && dns_name_equal(&fctx->domain, ns_name) &&
+ !dns_name_equal(ns_name, dns_rootname))
+ trim_ns_ttl(fctx, ns_name, ns_rdataset);
+
+ return (result);
+}
+
+static void
+fctx_increference(fetchctx_t *fctx) {
+ REQUIRE(VALID_FCTX(fctx));
+
+ LOCK(&fctx->res->buckets[fctx->bucketnum].lock);
+ fctx->references++;
+ UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock);
+}
+
+static bool
+fctx_decreference(fetchctx_t *fctx) {
+ bool bucket_empty = false;
+
+ REQUIRE(VALID_FCTX(fctx));
+
+ 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_unlink(fctx);
+ fctx_destroy(fctx);
+ } else {
+ /*
+ * Initiate shutdown.
+ */
+ fctx_shutdown(fctx);
+ }
+ }
+ return (bucket_empty);
+}
+
+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;
+ bool bucket_empty;
+ bool locked = 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;
+
+ /*
+ * Note: fevent->rdataset must be disassociated and
+ * isc_event_free(&event) be called before resuming
+ * processing of the 'fctx' to prevent use-after-free.
+ * 'fevent' is set to NULL so as to not have a dangling
+ * pointer.
+ */
+ if (fevent->result == ISC_R_CANCELED) {
+ if (dns_rdataset_isassociated(fevent->rdataset)) {
+ dns_rdataset_disassociate(fevent->rdataset);
+ }
+ fevent = NULL;
+ isc_event_free(&event);
+
+ dns_resolver_destroyfetch(&fctx->nsfetch);
+ fctx_done(fctx, ISC_R_CANCELED, __LINE__);
+ } 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);
+ fctx->ns_ttl = fctx->nameservers.ttl;
+ fctx->ns_ttl_ok = true;
+ log_ns_ttl(fctx, "resume_dslookup");
+
+ if (dns_rdataset_isassociated(fevent->rdataset)) {
+ dns_rdataset_disassociate(fevent->rdataset);
+ }
+ fevent = NULL;
+ isc_event_free(&event);
+
+ fcount_decr(fctx);
+ dns_name_free(&fctx->domain, fctx->mctx);
+ dns_name_init(&fctx->domain, NULL);
+ result = dns_name_dup(&fctx->nsname, fctx->mctx, &fctx->domain);
+ if (result != ISC_R_SUCCESS) {
+ fctx_done(fctx, DNS_R_SERVFAIL, __LINE__);
+ goto cleanup;
+ }
+ result = fcount_incr(fctx, true);
+ if (result != ISC_R_SUCCESS) {
+ fctx_done(fctx, DNS_R_SERVFAIL, __LINE__);
+ goto cleanup;
+ }
+ /*
+ * Try again.
+ */
+ fctx_try(fctx, true, false);
+ } 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)) {
+ if (dns_rdataset_isassociated(fevent->rdataset)) {
+ dns_rdataset_disassociate(fevent->rdataset);
+ }
+ fevent = NULL;
+ isc_event_free(&event);
+
+ fctx_done(fctx, DNS_R_SERVFAIL, __LINE__);
+ 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);
+ fevent = NULL;
+ isc_event_free(&event);
+
+ FCTXTRACE("continuing to look for parent's NS records");
+
+ result = dns_resolver_createfetch(fctx->res, &fctx->nsname,
+ dns_rdatatype_ns, domain,
+ nsrdataset, NULL,
+ fctx->options, task,
+ resume_dslookup, fctx,
+ &fctx->nsrrset, NULL,
+ &fctx->nsfetch);
+ /*
+ * fevent->rdataset (a.k.a. fctx->nsrrset) must not be
+ * accessed below this point to prevent races with
+ * another thread concurrently processing the fetch.
+ */
+ if (result != ISC_R_SUCCESS) {
+ fctx_done(fctx, result, __LINE__);
+ } else {
+ LOCK(&res->buckets[bucketnum].lock);
+ locked = true;
+ fctx->references++;
+ }
+ }
+
+ cleanup:
+ INSIST(event == NULL);
+ INSIST(fevent == NULL);
+ if (dns_rdataset_isassociated(&nameservers))
+ dns_rdataset_disassociate(&nameservers);
+ if (!locked)
+ LOCK(&res->buckets[bucketnum].lock);
+ bucket_empty = fctx_decreference(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,
+ 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 void
+log_nsid(isc_buffer_t *opt, size_t nsid_len, resquery_t *query,
+ int level, isc_mem_t *mctx)
+{
+ static const char hex[17] = "0123456789abcdef";
+ char addrbuf[ISC_SOCKADDR_FORMATSIZE];
+ uint16_t buflen, i;
+ unsigned char *p, *nsid;
+ unsigned char *buf = NULL, *pbuf = NULL;
+
+ /* Allocate buffer for storing hex version of the NSID */
+ buflen = (uint16_t)nsid_len * 2 + 1;
+ buf = isc_mem_get(mctx, buflen);
+ if (buf == NULL)
+ goto cleanup;
+ pbuf = isc_mem_get(mctx, nsid_len + 1);
+ if (pbuf == NULL)
+ goto cleanup;
+
+ /* Convert to hex */
+ p = buf;
+ nsid = isc_buffer_current(opt);
+ for (i = 0; i < nsid_len; i++) {
+ *p++ = hex[(nsid[i] >> 4) & 0xf];
+ *p++ = hex[nsid[i] & 0xf];
+ }
+ *p = '\0';
+
+ /* Make printable version */
+ p = pbuf;
+ for (i = 0; i < nsid_len; i++) {
+ if (isprint(nsid[i]))
+ *p++ = nsid[i];
+ else
+ *p++ = '.';
+ }
+ *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 (\"%s\") from %s", buf, pbuf, addrbuf);
+ cleanup:
+ if (pbuf != NULL)
+ isc_mem_put(mctx, pbuf, nsid_len + 1);
+ if (buf != NULL)
+ isc_mem_put(mctx, buf, buflen);
+}
+
+static bool
+iscname(fetchctx_t *fctx) {
+ isc_result_t result;
+
+ result = dns_message_findname(fctx->rmessage, DNS_SECTION_ANSWER,
+ &fctx->name, dns_rdatatype_cname, 0,
+ NULL, NULL);
+ return (result == ISC_R_SUCCESS ? true : false);
+}
+
+static bool
+betterreferral(fetchctx_t *fctx) {
+ isc_result_t result;
+ dns_name_t *name;
+ dns_rdataset_t *rdataset;
+ dns_message_t *message = fctx->rmessage;
+
+ for (result = dns_message_firstname(message, DNS_SECTION_AUTHORITY);
+ result == ISC_R_SUCCESS;
+ result = dns_message_nextname(message, DNS_SECTION_AUTHORITY)) {
+ name = NULL;
+ dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name);
+ if (!isstrictsubdomain(name, &fctx->domain))
+ continue;
+ for (rdataset = ISC_LIST_HEAD(name->list);
+ rdataset != NULL;
+ rdataset = ISC_LIST_NEXT(rdataset, link))
+ if (rdataset->type == dns_rdatatype_ns)
+ return (true);
+ }
+ return (false);
+}
+
+static void
+process_opt(resquery_t *query, dns_rdataset_t *opt) {
+ dns_rdata_t rdata;
+ isc_buffer_t optbuf;
+ isc_result_t result;
+ uint16_t optcode;
+ uint16_t optlen;
+ unsigned char *optvalue;
+ dns_adbaddrinfo_t *addrinfo;
+ unsigned char cookie[8];
+ bool seen_cookie = false;
+ bool seen_nsid = false;
+
+ result = dns_rdataset_first(opt);
+ if (result == ISC_R_SUCCESS) {
+ dns_rdata_init(&rdata);
+ dns_rdataset_current(opt, &rdata);
+ isc_buffer_init(&optbuf, rdata.data, rdata.length);
+ isc_buffer_add(&optbuf, rdata.length);
+ while (isc_buffer_remaininglength(&optbuf) >= 4) {
+ optcode = isc_buffer_getuint16(&optbuf);
+ optlen = isc_buffer_getuint16(&optbuf);
+ INSIST(optlen <= isc_buffer_remaininglength(&optbuf));
+ switch (optcode) {
+ case DNS_OPT_NSID:
+ if (!seen_nsid &&
+ query->options & DNS_FETCHOPT_WANTNSID)
+ log_nsid(&optbuf, optlen, query,
+ ISC_LOG_DEBUG(3),
+ query->fctx->res->mctx);
+ isc_buffer_forward(&optbuf, optlen);
+ seen_nsid = true;
+ break;
+ case DNS_OPT_COOKIE:
+ /*
+ * Only process the first cookie option.
+ */
+ if (seen_cookie) {
+ isc_buffer_forward(&optbuf, optlen);
+ break;
+ }
+ optvalue = isc_buffer_current(&optbuf);
+ compute_cc(query, cookie, sizeof(cookie));
+ INSIST(query->fctx->rmessage->cc_bad == 0 &&
+ query->fctx->rmessage->cc_ok == 0);
+ if (optlen >= 8U &&
+ memcmp(cookie, optvalue, 8) == 0) {
+ query->fctx->rmessage->cc_ok = 1;
+ inc_stats(query->fctx->res,
+ dns_resstatscounter_cookieok);
+ addrinfo = query->addrinfo;
+ dns_adb_setcookie(query->fctx->adb,
+ addrinfo, optvalue,
+ optlen);
+ } else
+ query->fctx->rmessage->cc_bad = 1;
+ isc_buffer_forward(&optbuf, optlen);
+ inc_stats(query->fctx->res,
+ dns_resstatscounter_cookiein);
+ seen_cookie = true;
+ break;
+ default:
+ isc_buffer_forward(&optbuf, optlen);
+ break;
+ }
+ }
+ INSIST(isc_buffer_remaininglength(&optbuf) == 0U);
+ }
+}
+
+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;
+ bool keep_trying, get_nameservers, resend, nextitem;
+ bool 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;
+ badnstype_t broken_type = badns_response;
+ bool no_response;
+ unsigned int bucketnum;
+ dns_resolver_t *res;
+ bool bucket_empty;
+#ifdef HAVE_DNSTAP
+ isc_socket_t *sock = NULL;
+ isc_sockaddr_t localaddr, *la = NULL;
+ unsigned char zone[DNS_NAME_MAXWIRE];
+ dns_dtmsgtype_t dtmsgtype;
+ dns_compress_t cctx;
+ isc_region_t zr;
+ isc_buffer_t zb;
+#endif /* HAVE_DNSTAP */
+
+ REQUIRE(VALID_QUERY(query));
+ fctx = query->fctx;
+ options = query->options;
+ REQUIRE(VALID_FCTX(fctx));
+ REQUIRE(event->ev_type == DNS_EVENT_DISPATCH);
+
+ QTRACE("response");
+
+ res = fctx->res;
+ if (isc_sockaddr_pf(&query->addrinfo->sockaddr) == PF_INET)
+ inc_stats(res, dns_resstatscounter_responsev4);
+ else
+ inc_stats(res, dns_resstatscounter_responsev6);
+
+ (void)isc_timer_touch(fctx->timer);
+
+ keep_trying = false;
+ broken_server = ISC_R_SUCCESS;
+ get_nameservers = false;
+ resend = false;
+ nextitem = false;
+ truncated = false;
+ finish = NULL;
+ no_response = false;
+
+ if (res->exiting) {
+ result = ISC_R_SHUTTINGDOWN;
+ FCTXTRACE("resolver shutting down");
+ goto done;
+ }
+
+ fctx->timeouts = 0;
+ fctx->timeout = false;
+ fctx->addrinfo = query->addrinfo;
+
+ /*
+ * 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 = true;
+ add_bad_edns(fctx, &query->addrinfo->sockaddr);
+ } else {
+ /*
+ * There's no hope for this query.
+ */
+ keep_trying = 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. Also adjust finish and
+ * no_response so that we penalize this address in SRTT
+ * adjustment later.
+ */
+ 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;
+ broken_type = badns_unreachable;
+ finish = NULL;
+ no_response = true;
+ }
+ }
+ FCTXTRACE3("dispatcher failure", devent->result);
+ goto done;
+ }
+
+ message = fctx->rmessage;
+
+ if (query->tsig != NULL) {
+ result = dns_message_setquerytsig(message, query->tsig);
+ if (result != ISC_R_SUCCESS) {
+ FCTXTRACE3("unable to set query tsig", result);
+ goto done;
+ }
+ }
+
+ if (query->tsigkey) {
+ result = dns_message_settsigkey(message, query->tsigkey);
+ if (result != ISC_R_SUCCESS) {
+ FCTXTRACE3("unable to set tsig key", result);
+ goto done;
+ }
+ }
+
+ dns_message_setclass(message, res->rdclass);
+
+ if ((options & DNS_FETCHOPT_TCP) == 0) {
+ if ((options & DNS_FETCHOPT_NOEDNS0) == 0)
+ dns_adb_setudpsize(fctx->adb, query->addrinfo,
+ isc_buffer_usedlength(&devent->buffer));
+ else
+ dns_adb_plainresponse(fctx->adb, query->addrinfo);
+ }
+ result = dns_message_parse(message, &devent->buffer, 0);
+ if (result != ISC_R_SUCCESS) {
+ FCTXTRACE3("message failed to parse", result);
+ 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 = true;
+ add_bad_edns(fctx,
+ &query->addrinfo->sockaddr);
+ inc_stats(res,
+ dns_resstatscounter_edns0fail);
+ } else {
+ broken_server = result;
+ keep_trying = true;
+ }
+ goto done;
+ }
+ /*
+ * We defer retrying via TCP for a bit so we can
+ * check out this message further.
+ */
+ truncated = 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 = true;
+ add_bad_edns(fctx, &query->addrinfo->sockaddr);
+ inc_stats(res, dns_resstatscounter_edns0fail);
+ } else {
+ broken_server = DNS_R_UNEXPECTEDRCODE;
+ keep_trying = true;
+ }
+ goto done;
+ default:
+ /*
+ * Something bad has happened.
+ */
+ goto done;
+ }
+ }
+
+ /*
+ * Log the incoming packet.
+ */
+ dns_message_logfmtpacket2(message, "received packet from",
+ &query->addrinfo->sockaddr,
+ DNS_LOGCATEGORY_RESOLVER,
+ DNS_LOGMODULE_PACKETS,
+ &dns_master_style_comment,
+ ISC_LOG_DEBUG(10), res->mctx);
+
+#ifdef HAVE_DNSTAP
+ /*
+ * Log the response via dnstap.
+ */
+ memset(&zr, 0, sizeof(zr));
+ result = dns_compress_init(&cctx, -1, res->mctx);
+ if (result == ISC_R_SUCCESS) {
+ isc_buffer_init(&zb, zone, sizeof(zone));
+ dns_compress_setmethods(&cctx, DNS_COMPRESS_NONE);
+ result = dns_name_towire(&fctx->domain, &cctx, &zb);
+ if (result == ISC_R_SUCCESS)
+ isc_buffer_usedregion(&zb, &zr);
+ dns_compress_invalidate(&cctx);
+ }
+
+ if ((fctx->qmessage->flags & DNS_MESSAGEFLAG_RD) != 0) {
+ dtmsgtype = DNS_DTTYPE_FR;
+ } else {
+ dtmsgtype = DNS_DTTYPE_RR;
+ }
+
+ if (query->exclusivesocket) {
+ sock = dns_dispatch_getentrysocket(query->dispentry);
+ } else {
+ sock = dns_dispatch_getsocket(query->dispatch);
+ }
+
+ if (sock != NULL) {
+ result = isc_socket_getsockname(sock, &localaddr);
+ if (result == ISC_R_SUCCESS) {
+ la = &localaddr;
+ }
+ }
+
+ dns_dt_send(res->view, dtmsgtype, la, &query->addrinfo->sockaddr,
+ (query->options & DNS_FETCHOPT_TCP),
+ &zr, &query->start, NULL, &devent->buffer);
+#endif /* HAVE_DNSTAP */
+
+ if (message->rdclass != res->rdclass) {
+ resend = true;
+ FCTXTRACE("bad class");
+ goto done;
+ }
+
+ /*
+ * Process receive opt record.
+ */
+ opt = dns_message_getopt(message);
+ if (opt != NULL)
+ process_opt(query, opt);
+
+ if (message->cc_bad && (options & DNS_FETCHOPT_TCP) == 0) {
+ /*
+ * If the COOKIE is bad, assume it is an attack and
+ * keep listening for a good answer.
+ */
+ nextitem = true;
+ if (isc_log_wouldlog(dns_lctx, ISC_LOG_INFO)) {
+ char addrbuf[ISC_SOCKADDR_FORMATSIZE];
+ isc_sockaddr_format(&query->addrinfo->sockaddr,
+ addrbuf, sizeof(addrbuf));
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
+ DNS_LOGMODULE_RESOLVER, ISC_LOG_INFO,
+ "bad cookie from %s", addrbuf);
+ }
+ goto done;
+ }
+
+ /*
+ * Is the question the same as the one we asked?
+ * NOERROR/NXDOMAIN/YXDOMAIN/REFUSED/SERVFAIL/BADCOOKIE must have
+ * the same question.
+ * FORMERR/NOTIMP if they have a question section then it must match.
+ */
+ switch (message->rcode) {
+ case dns_rcode_notimp:
+ case dns_rcode_formerr:
+ if (message->counts[DNS_SECTION_QUESTION] == 0)
+ break;
+ /* FALLTHROUGH */
+ case dns_rcode_nxrrset: /* Not expected. */
+ case dns_rcode_badcookie:
+ case dns_rcode_noerror:
+ case dns_rcode_nxdomain:
+ case dns_rcode_yxdomain:
+ case dns_rcode_refused:
+ case dns_rcode_servfail:
+ default:
+ result = same_question(fctx);
+ if (result != ISC_R_SUCCESS) {
+ FCTXTRACE3("response did not match question", result);
+ nextitem = true;
+ goto done;
+ }
+ break;
+ }
+
+ /*
+ * If the message is signed, check the signature. If not, this
+ * returns success anyway.
+ */
+ result = dns_message_checksig(message, res->view);
+ if (result != ISC_R_SUCCESS) {
+ FCTXTRACE3("signature check failed", result);
+ 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).
+ */
+
+ /*
+ * We have an affirmative response to the query and we have
+ * previously got a response from this server which indicated
+ * EDNS may not be supported so we can now cache the lack of
+ * EDNS support.
+ */
+ if (opt == NULL && !EDNSOK(query->addrinfo) &&
+ (message->rcode == dns_rcode_noerror ||
+ message->rcode == dns_rcode_nxdomain ||
+ message->rcode == dns_rcode_refused ||
+ message->rcode == dns_rcode_yxdomain) &&
+ bad_edns(fctx, &query->addrinfo->sockaddr)) {
+ dns_message_logpacket2(message,
+ "received packet (bad edns) from",
+ &query->addrinfo->sockaddr,
+ DNS_LOGCATEGORY_RESOLVER,
+ DNS_LOGMODULE_RESOLVER,
+ ISC_LOG_DEBUG(3),
+ res->mctx);
+ dns_adb_changeflags(fctx->adb, query->addrinfo,
+ DNS_FETCHOPT_NOEDNS0,
+ DNS_FETCHOPT_NOEDNS0);
+ } else if (opt == NULL && (message->flags & DNS_MESSAGEFLAG_TC) == 0 &&
+ !EDNSOK(query->addrinfo) &&
+ (message->rcode == dns_rcode_noerror ||
+ message->rcode == dns_rcode_nxdomain) &&
+ (query->options & DNS_FETCHOPT_NOEDNS0) == 0) {
+ /*
+ * We didn't get a OPT record in response to a EDNS query.
+ *
+ * Old versions of named incorrectly drop the OPT record
+ * when there is a signed, truncated response so we check
+ * that TC is not set.
+ *
+ * Record that the server is not talking EDNS. While this
+ * should be safe to do for any rcode we limit it to NOERROR
+ * and NXDOMAIN.
+ */
+ dns_message_logpacket2(message, "received packet (no opt) from",
+ &query->addrinfo->sockaddr,
+ DNS_LOGCATEGORY_RESOLVER,
+ DNS_LOGMODULE_RESOLVER,
+ ISC_LOG_DEBUG(3), res->mctx);
+ dns_adb_changeflags(fctx->adb, query->addrinfo,
+ DNS_FETCHOPT_NOEDNS0,
+ DNS_FETCHOPT_NOEDNS0);
+ }
+
+ /*
+ * If we get a non error EDNS response record the fact so we
+ * won't fallback to plain DNS in the future for this server.
+ */
+ if (opt != NULL && !EDNSOK(query->addrinfo) &&
+ (query->options & DNS_FETCHOPT_NOEDNS0) == 0 &&
+ (message->rcode == dns_rcode_noerror ||
+ message->rcode == dns_rcode_nxdomain ||
+ message->rcode == dns_rcode_refused ||
+ message->rcode == dns_rcode_yxdomain)) {
+ dns_adb_changeflags(fctx->adb, query->addrinfo,
+ FCTX_ADDRINFO_EDNSOK,
+ FCTX_ADDRINFO_EDNSOK);
+ }
+
+ /*
+ * Deal with truncated responses by retrying using TCP.
+ */
+ if ((message->flags & DNS_MESSAGEFLAG_TC) != 0)
+ truncated = true;
+
+ if (truncated) {
+ inc_stats(res, dns_resstatscounter_truncated);
+ if ((options & DNS_FETCHOPT_TCP) != 0) {
+ broken_server = DNS_R_TRUNCATEDTCP;
+ keep_trying = true;
+ } else {
+ options |= DNS_FETCHOPT_TCP;
+ resend = true;
+ }
+ FCTXTRACE3("message truncated", result);
+ goto done;
+ }
+
+ /*
+ * Is it a query response?
+ */
+ if (message->opcode != dns_opcode_query) {
+ /* XXXRTH Log */
+ broken_server = DNS_R_UNEXPECTEDOPCODE;
+ keep_trying = true;
+ FCTXTRACE("invalid message opcode");
+ goto done;
+ }
+
+ /*
+ * Update statistics about erroneous responses.
+ */
+ if (message->rcode != dns_rcode_noerror) {
+ switch (message->rcode) {
+ case dns_rcode_nxdomain:
+ inc_stats(res, dns_resstatscounter_nxdomain);
+ break;
+ case dns_rcode_servfail:
+ inc_stats(res, dns_resstatscounter_servfail);
+ break;
+ case dns_rcode_formerr:
+ inc_stats(res, dns_resstatscounter_formerr);
+ break;
+ case dns_rcode_refused:
+ inc_stats(res, dns_resstatscounter_refused);
+ break;
+ case dns_rcode_badvers:
+ inc_stats(res, dns_resstatscounter_badvers);
+ break;
+ case dns_rcode_badcookie:
+ inc_stats(res, dns_resstatscounter_badcookie);
+ break;
+ default:
+ inc_stats(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_yxdomain &&
+ message->rcode != dns_rcode_nxdomain) {
+ isc_buffer_t b;
+ char code[64];
+ unsigned char cookie[64];
+
+ /*
+ * Some servers do not ignore unknown EDNS options.
+ */
+ if (!NOCOOKIE(query->addrinfo) &&
+ (message->rcode == dns_rcode_formerr ||
+ message->rcode == dns_rcode_notimp ||
+ message->rcode == dns_rcode_refused) &&
+ dns_adb_getcookie(fctx->adb, query->addrinfo,
+ cookie, sizeof(cookie)) == 0U) {
+ dns_adb_changeflags(fctx->adb, query->addrinfo,
+ FCTX_ADDRINFO_NOCOOKIE,
+ FCTX_ADDRINFO_NOCOOKIE);
+ resend = true;
+ } else 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 = true;
+ /*
+ * Remember that they may not like EDNS0.
+ */
+ add_bad_edns(fctx, &query->addrinfo->sockaddr);
+ inc_stats(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 = 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.
+ */
+ log_formerr(fctx, "server sent FORMERR");
+ result = DNS_R_FORMERR;
+ }
+ } else if (message->rcode == dns_rcode_badvers) {
+ unsigned int version;
+ bool setnocookie = false;
+#if DNS_EDNS_VERSION > 0
+ unsigned int flags, mask;
+#endif
+
+ /*
+ * Some servers return BADVERS to unknown
+ * EDNS options. This cannot be long term
+ * strategy. Do not disable COOKIE if we have
+ * already have received a COOKIE from this
+ * server.
+ */
+ if (dns_adb_getcookie(fctx->adb, query->addrinfo,
+ cookie, sizeof(cookie)) == 0U) {
+ if (!NOCOOKIE(query->addrinfo))
+ setnocookie = true;
+ dns_adb_changeflags(fctx->adb, query->addrinfo,
+ FCTX_ADDRINFO_NOCOOKIE,
+ FCTX_ADDRINFO_NOCOOKIE);
+ }
+
+ INSIST(opt != NULL);
+ version = (opt->ttl >> 16) & 0xff;
+#if DNS_EDNS_VERSION > 0
+ flags = (version << DNS_FETCHOPT_EDNSVERSIONSHIFT) |
+ DNS_FETCHOPT_EDNSVERSIONSET;
+ mask = DNS_FETCHOPT_EDNSVERSIONMASK |
+ DNS_FETCHOPT_EDNSVERSIONSET;
+#endif
+
+ /*
+ * Record that we got a good EDNS response.
+ */
+ if (query->ednsversion > (int)version &&
+ !EDNSOK(query->addrinfo)) {
+ dns_adb_changeflags(fctx->adb, query->addrinfo,
+ FCTX_ADDRINFO_EDNSOK,
+ FCTX_ADDRINFO_EDNSOK);
+ }
+
+ /*
+ * RFC 2671 was not clear that unknown options should
+ * be ignored. RFC 6891 is clear that that they
+ * should be ignored. If we are supporting the
+ * experimental EDNS > 0 then perform strict
+ * version checking of badvers responses. We won't
+ * be sending COOKIE etc. in that case.
+ */
+#if DNS_EDNS_VERSION > 0
+ if ((int)version < query->ednsversion) {
+ dns_adb_changeflags(fctx->adb, query->addrinfo,
+ flags, mask);
+ resend = true;
+ } else {
+ broken_server = DNS_R_BADVERS;
+ keep_trying = true;
+ }
+#else
+ if (version == 0U && setnocookie) {
+ resend = true;
+ } else {
+ broken_server = DNS_R_BADVERS;
+ keep_trying = true;
+ }
+#endif
+ } else if (message->rcode == dns_rcode_badcookie &&
+ message->cc_ok) {
+ /*
+ * We have recorded the new cookie.
+ */
+ if (BADCOOKIE(query->addrinfo))
+ query->options |= DNS_FETCHOPT_TCP;
+ query->addrinfo->flags |= FCTX_ADDRINFO_BADCOOKIE;
+ resend = true;
+ } else {
+ /*
+ * XXXRTH log.
+ */
+ broken_server = DNS_R_UNEXPECTEDRCODE;
+ INSIST(broken_server != ISC_R_SUCCESS);
+ keep_trying = true;
+ }
+
+ isc_buffer_init(&b, code, sizeof(code) - 1);
+ dns_rcode_totext(fctx->rmessage->rcode, &b);
+ code[isc_buffer_usedlength(&b)] = '\0';
+ FCTXTRACE2("remote server broken: returned ", code);
+ goto done;
+ }
+
+ /*
+ * Is the server lame?
+ */
+ if (res->lame_ttl != 0 && !ISFORWARDER(query->addrinfo) &&
+ is_lame(fctx)) {
+ inc_stats(res, dns_resstatscounter_lame);
+ log_lame(fctx, query->addrinfo);
+ result = dns_adb_marklame(fctx->adb, query->addrinfo,
+ &fctx->name, fctx->type,
+ now + 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 = true;
+ FCTXTRACE("lame server");
+ goto done;
+ }
+
+ /*
+ * Enforce delegations only zones like NET and COM.
+ */
+ if (!ISFORWARDER(query->addrinfo) &&
+ dns_view_isdelegationonly(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(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 ((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_yxdomain ||
+ message->rcode == dns_rcode_nxdomain)) {
+ /*
+ * [normal case]
+ * We've got answers. If it has an authoritative answer or an
+ * answer from a forwarder, we're done.
+ */
+ if ((message->flags & DNS_MESSAGEFLAG_AA) != 0 ||
+ ISFORWARDER(query->addrinfo))
+ {
+ result = answer_response(fctx);
+ if (result != ISC_R_SUCCESS)
+ FCTXTRACE3("answer_response (AA/fwd)", result);
+ } else if (iscname(fctx) &&
+ fctx->type != dns_rdatatype_any &&
+ fctx->type != dns_rdatatype_cname)
+ {
+ /*
+ * A BIND8 server could return a non-authoritative
+ * answer when a CNAME is followed. We should treat
+ * it as a valid answer.
+ */
+ result = answer_response(fctx);
+ if (result != ISC_R_SUCCESS)
+ FCTXTRACE3("answer_response (!ANY/!CNAME)",
+ result);
+ } else if (fctx->type != dns_rdatatype_ns &&
+ !betterreferral(fctx)) {
+ /*
+ * Lame response !!!.
+ */
+ result = answer_response(fctx);
+ if (result != ISC_R_SUCCESS)
+ FCTXTRACE3("answer_response (!NS)", result);
+ } else {
+ if (fctx->type == dns_rdatatype_ns) {
+ /*
+ * A BIND 8 server could incorrectly return a
+ * non-authoritative answer to an NS query
+ * 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.
+ */
+ result = noanswer_response(fctx, NULL,
+ LOOK_FOR_NS_IN_ANSWER);
+ if (result != ISC_R_SUCCESS)
+ FCTXTRACE3("noanswer_response (NS)",
+ result);
+ } else {
+ /*
+ * Some other servers may still somehow include
+ * an answer when it should return a referral
+ * with an empty answer. Check to see if we can
+ * treat this as a referral by ignoring the
+ * answer. Further more, there may be an
+ * implementation that moves A/AAAA glue records
+ * to the answer section for that type of
+ * delegation when the query is for that glue
+ * record. LOOK_FOR_GLUE_IN_ANSWER will handle
+ * such a corner case.
+ */
+ result = noanswer_response(fctx, NULL,
+ LOOK_FOR_GLUE_IN_ANSWER);
+ if (result != ISC_R_SUCCESS)
+ FCTXTRACE3("noanswer_response", result);
+ }
+ if (result != DNS_R_DELEGATION) {
+ /*
+ * At this point, AA is not set, the response
+ * is not a referral, 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 = true;
+ goto done;
+ }
+ goto force_referral;
+ }
+ if (result != ISC_R_SUCCESS) {
+ if (result == DNS_R_FORMERR)
+ keep_trying = 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, 0);
+ switch (result) {
+ case ISC_R_SUCCESS:
+ case DNS_R_CHASEDSSERVERS:
+ break;
+ case DNS_R_DELEGATION:
+ force_referral:
+ /*
+ * We don't have the answer, but we know a better
+ * place to look.
+ */
+ get_nameservers = true;
+ keep_trying = true;
+ /*
+ * We have a new set of name servers, and it
+ * has not experienced any restarts yet.
+ */
+ fctx->restarts = 0;
+
+ /*
+ * Update local statistics counters collected for each
+ * new zone.
+ */
+ fctx->referrals++;
+ fctx->querysent = 0;
+ fctx->lamecount = 0;
+ fctx->quotacount = 0;
+ fctx->neterr = 0;
+ fctx->badresp = 0;
+ fctx->adberr = 0;
+
+ result = ISC_R_SUCCESS;
+ break;
+ default:
+ /*
+ * Something has gone wrong.
+ */
+ if (result == DNS_R_FORMERR)
+ keep_trying = true;
+ FCTXTRACE3("noanswer_response", result);
+ goto done;
+ }
+ } else {
+ /*
+ * The server is insane.
+ */
+ /* XXXRTH Log */
+ broken_server = DNS_R_UNEXPECTEDRCODE;
+ keep_trying = true;
+ FCTXTRACE("broken server: unexpected rcode");
+ 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) {
+ FCTXTRACE3("cache_message complete", result);
+ 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;
+
+ /*
+ * Cache DS NXDOMAIN seperately to other types.
+ */
+ if (message->rcode == dns_rcode_nxdomain &&
+ fctx->type != dns_rdatatype_ds)
+ covers = dns_rdatatype_any;
+ else
+ covers = fctx->type;
+
+ /*
+ * Cache any negative cache entries in the message.
+ */
+ result = ncache_message(fctx, query->addrinfo, covers, now);
+ if (result != ISC_R_SUCCESS)
+ FCTXTRACE3("ncache_message complete", result);
+ }
+
+ done:
+ /*
+ * Remember the query's addrinfo, in case we need to mark the
+ * server as broken.
+ */
+ addrinfo = query->addrinfo;
+
+ FCTXTRACE4("query canceled in response(); ",
+ no_response ? "no response" : "responding",
+ result);
+
+ /*
+ * Cancel the query.
+ *
+ * XXXRTH Don't cancel the query if waiting for validation?
+ */
+ if (!nextitem)
+ fctx_cancelquery(&query, &devent, finish,
+ no_response, false);
+
+#ifdef ENABLE_AFL
+ if (dns_fuzzing_resolver && (keep_trying || resend)) {
+ fctx_done(fctx, DNS_R_SERVFAIL, __LINE__);
+ return;
+ } else
+#endif
+ 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, broken_type);
+ }
+
+ if (get_nameservers) {
+ dns_name_t *name;
+ fname = dns_fixedname_initname(&foundname);
+ if (result != ISC_R_SUCCESS) {
+ fctx_done(fctx, DNS_R_SERVFAIL, __LINE__);
+ 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(res->view,
+ name, fname,
+ now, findoptions,
+ true,
+ &fctx->nameservers,
+ NULL);
+ if (result != ISC_R_SUCCESS) {
+ FCTXTRACE("couldn't find a zonecut");
+ fctx_done(fctx, DNS_R_SERVFAIL, __LINE__);
+ 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, __LINE__);
+ return;
+ }
+
+ fcount_decr(fctx);
+ dns_name_free(&fctx->domain, fctx->mctx);
+ dns_name_init(&fctx->domain, NULL);
+ result = dns_name_dup(fname, fctx->mctx, &fctx->domain);
+ if (result != ISC_R_SUCCESS) {
+ fctx_done(fctx, DNS_R_SERVFAIL, __LINE__);
+ return;
+ }
+ result = fcount_incr(fctx, true);
+ if (result != ISC_R_SUCCESS) {
+ fctx_done(fctx, DNS_R_SERVFAIL, __LINE__);
+ return;
+ }
+ fctx->ns_ttl = fctx->nameservers.ttl;
+ fctx->ns_ttl_ok = true;
+ fctx_cancelqueries(fctx, true, false);
+ fctx_cleanupfinds(fctx);
+ fctx_cleanupaltfinds(fctx);
+ fctx_cleanupforwaddrs(fctx);
+ fctx_cleanupaltaddrs(fctx);
+ }
+ /*
+ * Try again.
+ */
+ fctx_try(fctx, !get_nameservers, false);
+ } else if (resend) {
+ /*
+ * Resend (probably with changed options).
+ */
+ FCTXTRACE("resend");
+ inc_stats(res, dns_resstatscounter_retry);
+ bucketnum = fctx->bucketnum;
+ fctx_increference(fctx);
+ result = fctx_query(fctx, addrinfo, options);
+ if (result != ISC_R_SUCCESS) {
+ fctx_done(fctx, result, __LINE__);
+ LOCK(&res->buckets[bucketnum].lock);
+ bucket_empty = fctx_decreference(fctx);
+ UNLOCK(&res->buckets[bucketnum].lock);
+ if (bucket_empty)
+ empty_bucket(res);
+ }
+ } else if (nextitem) {
+ /*
+ * Wait for next item.
+ */
+ FCTXTRACE("nextitem");
+ inc_stats(fctx->res, dns_resstatscounter_nextitem);
+ INSIST(query->dispentry != NULL);
+ dns_message_reset(fctx->rmessage, DNS_MESSAGE_INTENTPARSE);
+ result = dns_dispatch_getnext(query->dispentry, &devent);
+ if (result != ISC_R_SUCCESS)
+ fctx_done(fctx, result, __LINE__);
+ } 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, true, false);
+ /*
+ * 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, __LINE__);
+ } else if (result == DNS_R_CHASEDSSERVERS) {
+ unsigned int n;
+ add_bad(fctx, addrinfo, result, broken_type);
+ fctx_cancelqueries(fctx, true, false);
+ 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(res, &fctx->nsname,
+ dns_rdatatype_ns,
+ NULL, NULL, NULL,
+ fctx->options, task,
+ resume_dslookup, fctx,
+ &fctx->nsrrset, NULL,
+ &fctx->nsfetch);
+ if (result != ISC_R_SUCCESS)
+ fctx_done(fctx, result, __LINE__);
+ else {
+ fctx_increference(fctx);
+ result = fctx_stopidletimer(fctx);
+ if (result != ISC_R_SUCCESS)
+ fctx_done(fctx, result, __LINE__);
+ }
+ } else {
+ /*
+ * We're done.
+ */
+ fctx_done(fctx, result, __LINE__);
+ }
+}
+
+/***
+ *** 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));
+ for (i = 0; i < RES_DOMAIN_BUCKETS; i++) {
+ INSIST(ISC_LIST_EMPTY(res->dbuckets[i].list));
+ isc_mem_detach(&res->dbuckets[i].mctx);
+ DESTROYLOCK(&res->dbuckets[i].lock);
+ }
+ isc_mem_put(res->mctx, res->dbuckets,
+ RES_DOMAIN_BUCKETS * sizeof(zonebucket_t));
+ if (res->dispatches4 != NULL)
+ dns_dispatchset_destroy(&res->dispatches4);
+ if (res->dispatches6 != NULL)
+ dns_dispatchset_destroy(&res->dispatches6);
+ 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_reset_ds_digests(res);
+ dns_badcache_destroy(&res->badcache);
+ 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;
+ bool logit = false;
+
+ REQUIRE(VALID_RESOLVER(res));
+
+ UNUSED(task);
+
+ LOCK(&res->lock);
+ INSIST(!res->exiting);
+ if (res->spillat > res->spillatmin) {
+ res->spillat--;
+ logit = true;
+ }
+ if (res->spillat <= res->spillatmin) {
+ result = isc_timer_reset(res->spillattimer,
+ isc_timertype_inactive, NULL,
+ NULL, 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, unsigned int ndisp,
+ 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, dbuckets_created = 0;
+ isc_task_t *task = NULL;
+ char name[16];
+ unsigned dispattr;
+
+ /*
+ * Create a resolver.
+ */
+
+ REQUIRE(DNS_VIEW_VALID(view));
+ REQUIRE(ntasks > 0);
+ REQUIRE(ndisp > 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->digests = NULL;
+ res->badcache = NULL;
+ dns_badcache_init(res->mctx, DNS_RESOLVER_BADCACHESIZE,
+ &res->badcache);
+ res->mustbesecure = NULL;
+ res->spillatmin = res->spillat = 10;
+ res->spillatmax = 100;
+ res->spillattimer = NULL;
+ res->zspill = 0;
+ res->zero_no_soa_ttl = false;
+ res->query_timeout = DEFAULT_QUERY_TIMEOUT;
+ res->maxdepth = DEFAULT_RECURSION_DEPTH;
+ res->maxqueries = DEFAULT_MAX_QUERIES;
+ res->quotaresp[dns_quotatype_zone] = DNS_R_DROP;
+ res->quotaresp[dns_quotatype_server] = DNS_R_SERVFAIL;
+ res->nbuckets = ntasks;
+ if (view->resstats != NULL)
+ isc_stats_set(view->resstats, ntasks,
+ dns_resstatscounter_buckets);
+ 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 = false;
+ buckets_created++;
+ }
+
+ res->dbuckets = isc_mem_get(view->mctx,
+ RES_DOMAIN_BUCKETS * sizeof(zonebucket_t));
+ if (res->dbuckets == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup_buckets;
+ }
+ for (i = 0; i < RES_DOMAIN_BUCKETS; i++) {
+ ISC_LIST_INIT(res->dbuckets[i].list);
+ res->dbuckets[i].mctx = NULL;
+ isc_mem_attach(view->mctx, &res->dbuckets[i].mctx);
+ result = isc_mutex_init(&res->dbuckets[i].lock);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_detach(&res->dbuckets[i].mctx);
+ goto cleanup_dbuckets;
+ }
+ dbuckets_created++;
+ }
+
+ res->dispatches4 = NULL;
+ if (dispatchv4 != NULL) {
+ dns_dispatchset_create(view->mctx, socketmgr, taskmgr,
+ dispatchv4, &res->dispatches4, ndisp);
+ dispattr = dns_dispatch_getattributes(dispatchv4);
+ res->exclusivev4 = (dispattr & DNS_DISPATCHATTR_EXCLUSIVE);
+ }
+
+ res->dispatches6 = NULL;
+ if (dispatchv6 != NULL) {
+ dns_dispatchset_create(view->mctx, socketmgr, taskmgr,
+ dispatchv6, &res->dispatches6, ndisp);
+ dispattr = dns_dispatch_getattributes(dispatchv6);
+ res->exclusivev6 = (dispattr & DNS_DISPATCHATTR_EXCLUSIVE);
+ }
+
+ res->querydscp4 = -1;
+ res->querydscp6 = -1;
+ res->references = 1;
+ res->exiting = false;
+ res->frozen = false;
+ ISC_LIST_INIT(res->whenshutdown);
+ res->priming = 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;
+ isc_task_setname(task, "resolver_task", NULL);
+
+ 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)
+#if USE_ALGLOCK
+ goto cleanup_alglock;
+#else
+ goto cleanup_spillattimer;
+#endif
+#endif
+
+ res->magic = RES_MAGIC;
+
+ *resp = res;
+
+ return (ISC_R_SUCCESS);
+
+#if USE_ALGLOCK && USE_MBSLOCK
+ cleanup_alglock:
+ isc_rwlock_destroy(&res->alglock);
+#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->dispatches6 != NULL)
+ dns_dispatchset_destroy(&res->dispatches6);
+ if (res->dispatches4 != NULL)
+ dns_dispatchset_destroy(&res->dispatches4);
+
+ cleanup_dbuckets:
+ for (i = 0; i < dbuckets_created; i++) {
+ DESTROYLOCK(&res->dbuckets[i].lock);
+ isc_mem_detach(&res->dbuckets[i].mctx);
+ }
+ isc_mem_put(view->mctx, res->dbuckets,
+ RES_DOMAIN_BUCKETS * sizeof(zonebucket_t));
+
+ 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));
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
+ DNS_LOGMODULE_RESOLVER, ISC_LOG_INFO,
+ "resolver priming query complete");
+
+ UNUSED(task);
+
+ LOCK(&res->lock);
+
+ INSIST(res->priming);
+ res->priming = 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) {
+ bool want_priming = 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 = true;
+ want_priming = 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 = 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) {
+ isc_mem_put(res->mctx, rdataset, sizeof(*rdataset));
+ LOCK(&res->lock);
+ INSIST(res->priming);
+ res->priming = false;
+ UNLOCK(&res->lock);
+ }
+ }
+}
+
+void
+dns_resolver_freeze(dns_resolver_t *res) {
+ /*
+ * Freeze resolver.
+ */
+
+ REQUIRE(VALID_RESOLVER(res));
+
+ res->frozen = 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 *tclone;
+ 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 {
+ tclone = NULL;
+ isc_task_attach(task, &tclone);
+ event->ev_sender = tclone;
+ 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_result_t result;
+
+ REQUIRE(VALID_RESOLVER(res));
+
+ RTRACE("shutdown");
+
+ LOCK(&res->lock);
+
+ if (!res->exiting) {
+ RTRACE("exiting");
+ res->exiting = 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->dispatches4 != NULL && !res->exclusivev4) {
+ dns_dispatchset_cancelall(res->dispatches4,
+ res->buckets[i].task);
+ }
+ if (res->dispatches6 != NULL && !res->exclusivev6) {
+ dns_dispatchset_cancelall(res->dispatches6,
+ res->buckets[i].task);
+ }
+ res->buckets[i].exiting = 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, true);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ }
+
+ UNLOCK(&res->lock);
+}
+
+void
+dns_resolver_detach(dns_resolver_t **resp) {
+ dns_resolver_t *res;
+ bool need_destroy = 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 = true;
+ }
+
+ UNLOCK(&res->lock);
+
+ if (need_destroy)
+ destroy(res);
+
+ *resp = NULL;
+}
+
+static inline bool
+fctx_match(fetchctx_t *fctx, dns_name_t *name, dns_rdatatype_t type,
+ unsigned int options)
+{
+ /*
+ * Don't match fetch contexts that are shutting down.
+ */
+ if (fctx->cloned || fctx->state == fetchstate_done ||
+ ISC_LIST_EMPTY(fctx->events))
+ return (false);
+
+ if (fctx->type != type || fctx->options != options)
+ return (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 there's no chance of logging it, don't render (format) the
+ * name and RDATA type (further below), and return early.
+ */
+ 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,
+ "fetch: %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_createfetch3(res, name, type, domain,
+ nameservers, forwarders, NULL, 0,
+ options, 0, NULL, 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)
+{
+ return (dns_resolver_createfetch3(res, name, type, domain,
+ nameservers, forwarders, client, id,
+ options, 0, NULL, task, action, arg,
+ rdataset, sigrdataset, fetchp));
+}
+
+isc_result_t
+dns_resolver_createfetch3(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, unsigned int depth,
+ isc_counter_t *qc, 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;
+ bool new_fctx = false;
+ isc_event_t *event;
+ unsigned int count = 0;
+ unsigned int spillat;
+ unsigned int spillatmin;
+ bool dodestroy = false;
+
+ 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);
+ fetch->mctx = NULL;
+ isc_mem_attach(res->mctx, &fetch->mctx);
+
+ bucketnum = dns_name_fullhash(name, 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 = true;
+ if (fctx->spilled) {
+ result = DNS_R_DROP;
+ goto unlock;
+ }
+ }
+
+ if (fctx == NULL) {
+ result = fctx_create(res, name, type, domain, nameservers,
+ options, bucketnum, depth, qc, &fctx);
+ if (result != ISC_R_SUCCESS)
+ goto unlock;
+ new_fctx = true;
+ } else if (fctx->depth > depth)
+ fctx->depth = depth;
+
+ 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_unlink()
+ * since we know we're not exiting.
+ */
+ (void)fctx_unlink(fctx);
+ dodestroy = true;
+ }
+ }
+
+ unlock:
+ UNLOCK(&res->buckets[bucketnum].lock);
+
+ if (dodestroy)
+ fctx_destroy(fctx);
+
+ if (result == ISC_R_SUCCESS) {
+ FTRACE("created");
+ *fetchp = fetch;
+ } else
+ isc_mem_putanddetach(&fetch->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;
+ bool bucket_empty;
+
+ 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);
+ }
+ }
+
+ bucket_empty = fctx_decreference(fctx);
+
+ UNLOCK(&res->buckets[bucketnum].lock);
+
+ isc_mem_putanddetach(&fetch->mctx, fetch, sizeof(*fetch));
+ *fetchp = NULL;
+
+ if (bucket_empty)
+ empty_bucket(res);
+}
+
+void
+dns_resolver_logfetch(dns_fetch_t *fetch, isc_log_t *lctx,
+ isc_logcategory_t *category, isc_logmodule_t *module,
+ int level, bool duplicateok)
+{
+ fetchctx_t *fctx;
+ dns_resolver_t *res;
+ char domainbuf[DNS_NAME_FORMATSIZE];
+
+ REQUIRE(DNS_FETCH_VALID(fetch));
+ fctx = fetch->private;
+ REQUIRE(VALID_FCTX(fctx));
+ res = fctx->res;
+
+ LOCK(&res->buckets[fctx->bucketnum].lock);
+
+ INSIST(fctx->exitline >= 0);
+ if (!fctx->logged || duplicateok) {
+ dns_name_format(&fctx->domain, domainbuf, sizeof(domainbuf));
+ isc_log_write(lctx, category, module, level,
+ "fetch completed at %s:%d for %s in "
+ "%" PRIu64 "."
+ "%06" PRIu64 ": %s/%s "
+ "[domain:%s,referral:%u,restart:%u,qrysent:%u,"
+ "timeout:%u,lame:%u,quota:%u,neterr:%u,"
+ "badresp:%u,adberr:%u,findfail:%u,valfail:%u]",
+ __FILE__, fctx->exitline, fctx->info,
+ fctx->duration / US_PER_SEC,
+ fctx->duration % US_PER_SEC,
+ isc_result_totext(fctx->result),
+ isc_result_totext(fctx->vresult), domainbuf,
+ fctx->referrals, fctx->restarts,
+ fctx->querysent, fctx->timeouts,
+ fctx->lamecount, fctx->quotacount,
+ fctx->neterr, fctx->badresp, fctx->adberr,
+ fctx->findfail, fctx->valfail);
+ fctx->logged = true;
+ }
+
+ UNLOCK(&res->buckets[fctx->bucketnum].lock);
+}
+
+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 (dns_dispatchset_get(resolver->dispatches4));
+}
+
+dns_dispatch_t *
+dns_resolver_dispatchv6(dns_resolver_t *resolver) {
+ REQUIRE(VALID_RESOLVER(resolver));
+ return (dns_dispatchset_get(resolver->dispatches6));
+}
+
+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);
+}
+
+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, 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 = true;
+ a->_u.addr = *alt;
+ } else {
+ a->isaddress = 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, uint16_t udpsize) {
+ REQUIRE(VALID_RESOLVER(resolver));
+ resolver->udpsize = udpsize;
+}
+
+uint16_t
+dns_resolver_getudpsize(dns_resolver_t *resolver) {
+ REQUIRE(VALID_RESOLVER(resolver));
+ return (resolver->udpsize);
+}
+
+void
+dns_resolver_flushbadcache(dns_resolver_t *resolver, dns_name_t *name) {
+ if (name != NULL)
+ dns_badcache_flushname(resolver->badcache, name);
+ else
+ dns_badcache_flush(resolver->badcache);
+}
+
+void
+dns_resolver_flushbadnames(dns_resolver_t *resolver, dns_name_t *name) {
+ dns_badcache_flushtree(resolver->badcache, name);
+}
+
+void
+dns_resolver_addbadcache(dns_resolver_t *resolver, dns_name_t *name,
+ dns_rdatatype_t type, isc_time_t *expire)
+{
+#ifdef ENABLE_AFL
+ if (!dns_fuzzing_resolver)
+#endif
+ {
+ (void) dns_badcache_add(resolver->badcache, name, type,
+ false, 0, expire);
+ }
+}
+
+bool
+dns_resolver_getbadcache(dns_resolver_t *resolver, dns_name_t *name,
+ dns_rdatatype_t type, isc_time_t *now)
+{
+ return (dns_badcache_find(resolver->badcache, name, type, NULL, now));
+}
+
+void
+dns_resolver_printbadcache(dns_resolver_t *resolver, FILE *fp) {
+ (void) dns_badcache_print(resolver->badcache, "Bad cache", fp);
+}
+
+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 *tmp;
+ unsigned char *algorithms;
+ isc_result_t result;
+ dns_rbtnode_t *node = NULL;
+
+ /*
+ * Whether an algorithm is disabled (or not) is stored in a
+ * per-name bitfield that is stored as the node data of an
+ * RBT.
+ */
+
+ 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 is set, algorithms[0] contains its
+ * length.
+ */
+ if (algorithms == NULL || len > *algorithms) {
+ /*
+ * If no bitfield exists in the node data, or if
+ * it is not long enough, allocate a new
+ * bitfield and copy the old (smaller) bitfield
+ * into it if one exists.
+ */
+ tmp = isc_mem_get(resolver->mctx, len);
+ if (tmp == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ memset(tmp, 0, len);
+ if (algorithms != NULL)
+ memmove(tmp, algorithms, *algorithms);
+ tmp[len-1] |= mask;
+ /* 'tmp[0]' should contain the length of 'tmp'. */
+ *tmp = len;
+ node->data = tmp;
+ /* Free the older bitfield. */
+ 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);
+}
+
+bool
+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;
+ bool found = false;
+
+ REQUIRE(VALID_RESOLVER(resolver));
+
+ /*
+ * DH is unsupported for DNSKEYs, see RFC 4034 sec. A.1.
+ */
+ if ((alg == DST_ALG_DH) || (alg == DST_ALG_INDIRECT))
+ return (false);
+
+#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 = true;
+ }
+ unlock:
+#if USE_ALGLOCK
+ RWUNLOCK(&resolver->alglock, isc_rwlocktype_read);
+#endif
+ if (found)
+ return (false);
+
+ return (dst_algorithm_supported(alg));
+}
+
+static void
+free_digest(void *node, void *arg) {
+ unsigned char *digests = node;
+ isc_mem_t *mctx = arg;
+
+ isc_mem_put(mctx, digests, *digests);
+}
+
+void
+dns_resolver_reset_ds_digests(dns_resolver_t *resolver) {
+
+ REQUIRE(VALID_RESOLVER(resolver));
+
+#if USE_ALGLOCK
+ RWLOCK(&resolver->alglock, isc_rwlocktype_write);
+#endif
+ if (resolver->digests != NULL)
+ dns_rbt_destroy(&resolver->digests);
+#if USE_ALGLOCK
+ RWUNLOCK(&resolver->alglock, isc_rwlocktype_write);
+#endif
+}
+
+isc_result_t
+dns_resolver_disable_ds_digest(dns_resolver_t *resolver, dns_name_t *name,
+ unsigned int digest_type)
+{
+ unsigned int len, mask;
+ unsigned char *tmp;
+ unsigned char *digests;
+ isc_result_t result;
+ dns_rbtnode_t *node = NULL;
+
+ /*
+ * Whether a digest is disabled (or not) is stored in a per-name
+ * bitfield that is stored as the node data of an RBT.
+ */
+
+ REQUIRE(VALID_RESOLVER(resolver));
+ if (digest_type > 255)
+ return (ISC_R_RANGE);
+
+#if USE_ALGLOCK
+ RWLOCK(&resolver->alglock, isc_rwlocktype_write);
+#endif
+ if (resolver->digests == NULL) {
+ result = dns_rbt_create(resolver->mctx, free_digest,
+ resolver->mctx, &resolver->digests);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+
+ len = digest_type/8 + 2;
+ mask = 1 << (digest_type%8);
+
+ result = dns_rbt_addnode(resolver->digests, name, &node);
+
+ if (result == ISC_R_SUCCESS || result == ISC_R_EXISTS) {
+ digests = node->data;
+ /* If digests is set, digests[0] contains its length. */
+ if (digests == NULL || len > *digests) {
+ /*
+ * If no bitfield exists in the node data, or if
+ * it is not long enough, allocate a new
+ * bitfield and copy the old (smaller) bitfield
+ * into it if one exists.
+ */
+ tmp = isc_mem_get(resolver->mctx, len);
+ if (tmp == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ memset(tmp, 0, len);
+ if (digests != NULL)
+ memmove(tmp, digests, *digests);
+ tmp[len-1] |= mask;
+ /* tmp[0] should contain the length of 'tmp'. */
+ *tmp = len;
+ node->data = tmp;
+ /* Free the older bitfield. */
+ if (digests != NULL)
+ isc_mem_put(resolver->mctx, digests,
+ *digests);
+ } else
+ digests[len-1] |= mask;
+ }
+ result = ISC_R_SUCCESS;
+ cleanup:
+#if USE_ALGLOCK
+ RWUNLOCK(&resolver->alglock, isc_rwlocktype_write);
+#endif
+ return (result);
+}
+
+bool
+dns_resolver_ds_digest_supported(dns_resolver_t *resolver, dns_name_t *name,
+ unsigned int digest_type)
+{
+ unsigned int len, mask;
+ unsigned char *digests;
+ void *data = NULL;
+ isc_result_t result;
+ bool found = false;
+
+ REQUIRE(VALID_RESOLVER(resolver));
+
+#if USE_ALGLOCK
+ RWLOCK(&resolver->alglock, isc_rwlocktype_read);
+#endif
+ if (resolver->digests == NULL)
+ goto unlock;
+ result = dns_rbt_findname(resolver->digests, name, 0, NULL, &data);
+ if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
+ len = digest_type/8 + 2;
+ mask = 1 << (digest_type%8);
+ digests = data;
+ if (len <= *digests && (digests[len-1] & mask) != 0)
+ found = true;
+ }
+ unlock:
+#if USE_ALGLOCK
+ RWUNLOCK(&resolver->alglock, isc_rwlocktype_read);
+#endif
+ if (found)
+ return (false);
+ return (dst_ds_digest_supported(digest_type));
+}
+
+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 bool yes = true, no = false;
+
+isc_result_t
+dns_resolver_setmustbesecure(dns_resolver_t *resolver, dns_name_t *name,
+ bool 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);
+}
+
+bool
+dns_resolver_getmustbesecure(dns_resolver_t *resolver, dns_name_t *name) {
+ void *data = NULL;
+ bool value = 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 = *(bool*)data;
+ unlock:
+#if USE_MBSLOCK
+ RWUNLOCK(&resolver->mbslock, isc_rwlocktype_read);
+#endif
+ return (value);
+}
+
+void
+dns_resolver_getclientsperquery(dns_resolver_t *resolver, uint32_t *cur,
+ uint32_t *min, 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, uint32_t min,
+ uint32_t max)
+{
+ REQUIRE(VALID_RESOLVER(resolver));
+
+ LOCK(&resolver->lock);
+ resolver->spillatmin = resolver->spillat = min;
+ resolver->spillatmax = max;
+ UNLOCK(&resolver->lock);
+}
+
+void
+dns_resolver_setfetchesperzone(dns_resolver_t *resolver, uint32_t clients)
+{
+ REQUIRE(VALID_RESOLVER(resolver));
+
+ LOCK(&resolver->lock);
+ resolver->zspill = clients;
+ UNLOCK(&resolver->lock);
+}
+
+
+bool
+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, bool 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);
+}
+
+unsigned int
+dns_resolver_gettimeout(dns_resolver_t *resolver) {
+ REQUIRE(VALID_RESOLVER(resolver));
+
+ return (resolver->query_timeout);
+}
+
+void
+dns_resolver_settimeout(dns_resolver_t *resolver, unsigned int seconds) {
+ REQUIRE(VALID_RESOLVER(resolver));
+
+ if (seconds == 0)
+ seconds = DEFAULT_QUERY_TIMEOUT;
+ if (seconds > MAXIMUM_QUERY_TIMEOUT)
+ seconds = MAXIMUM_QUERY_TIMEOUT;
+ if (seconds < MINIMUM_QUERY_TIMEOUT)
+ seconds = MINIMUM_QUERY_TIMEOUT;
+
+ resolver->query_timeout = seconds;
+}
+
+void
+dns_resolver_setquerydscp4(dns_resolver_t *resolver, isc_dscp_t dscp) {
+ REQUIRE(VALID_RESOLVER(resolver));
+
+ resolver->querydscp4 = dscp;
+}
+
+isc_dscp_t
+dns_resolver_getquerydscp4(dns_resolver_t *resolver) {
+ REQUIRE(VALID_RESOLVER(resolver));
+ return (resolver->querydscp4);
+}
+
+void
+dns_resolver_setquerydscp6(dns_resolver_t *resolver, isc_dscp_t dscp) {
+ REQUIRE(VALID_RESOLVER(resolver));
+
+ resolver->querydscp6 = dscp;
+}
+
+isc_dscp_t
+dns_resolver_getquerydscp6(dns_resolver_t *resolver) {
+ REQUIRE(VALID_RESOLVER(resolver));
+ return (resolver->querydscp6);
+}
+
+void
+dns_resolver_setmaxdepth(dns_resolver_t *resolver, unsigned int maxdepth) {
+ REQUIRE(VALID_RESOLVER(resolver));
+ resolver->maxdepth = maxdepth;
+}
+
+unsigned int
+dns_resolver_getmaxdepth(dns_resolver_t *resolver) {
+ REQUIRE(VALID_RESOLVER(resolver));
+ return (resolver->maxdepth);
+}
+
+void
+dns_resolver_setmaxqueries(dns_resolver_t *resolver, unsigned int queries) {
+ REQUIRE(VALID_RESOLVER(resolver));
+ resolver->maxqueries = queries;
+}
+
+unsigned int
+dns_resolver_getmaxqueries(dns_resolver_t *resolver) {
+ REQUIRE(VALID_RESOLVER(resolver));
+ return (resolver->maxqueries);
+}
+
+void
+dns_resolver_dumpfetches(dns_resolver_t *resolver,
+ isc_statsformat_t format, FILE *fp)
+{
+ int i;
+
+ REQUIRE(VALID_RESOLVER(resolver));
+ REQUIRE(fp != NULL);
+ REQUIRE(format == isc_statsformat_file);
+
+ for (i = 0; i < RES_DOMAIN_BUCKETS; i++) {
+ fctxcount_t *fc;
+ LOCK(&resolver->dbuckets[i].lock);
+ for (fc = ISC_LIST_HEAD(resolver->dbuckets[i].list);
+ fc != NULL;
+ fc = ISC_LIST_NEXT(fc, link))
+ {
+ dns_name_print(fc->domain, fp);
+ fprintf(fp, ": %u active (%u spilled, %u allowed)\n",
+ fc->count, fc->dropped, fc->allowed);
+ }
+ UNLOCK(&resolver->dbuckets[i].lock);
+ }
+}
+
+void
+dns_resolver_setquotaresponse(dns_resolver_t *resolver,
+ dns_quotatype_t which, isc_result_t resp)
+{
+ REQUIRE(VALID_RESOLVER(resolver));
+ REQUIRE(which == dns_quotatype_zone || which == dns_quotatype_server);
+ REQUIRE(resp == DNS_R_DROP || resp == DNS_R_SERVFAIL);
+
+ resolver->quotaresp[which] = resp;
+}
+
+isc_result_t
+dns_resolver_getquotaresponse(dns_resolver_t *resolver, dns_quotatype_t which)
+{
+ REQUIRE(VALID_RESOLVER(resolver));
+ REQUIRE(which == dns_quotatype_zone || which == dns_quotatype_server);
+
+ return (resolver->quotaresp[which]);
+}
diff --git a/lib/dns/result.c b/lib/dns/result.c
new file mode 100644
index 0000000..e7507c5
--- /dev/null
+++ b/lib/dns/result.c
@@ -0,0 +1,443 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \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 */
+ "insecurity proof failed", /*%< 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 */
+
+ "not master", /*%< 105 DNS_R_NOTMASTER */
+ "broken trust chain", /*%< 106 DNS_R_BROKENCHAIN */
+ "expired", /*%< 107 DNS_R_EXPIRED */
+ "not dynamic", /*%< 108 DNS_R_NOTDYNAMIC */
+ "bad EUI", /*%< 109 DNS_R_BADEUI */
+
+ "covered by negative trust anchor", /*%< 110 DNS_R_NTACOVERED */
+ "bad CDS", /*%< 111 DNS_R_BADCDS */
+ "bad CDNSKEY", /*%< 112 DNS_R_BADCDNSKEY */
+ "malformed OPT option", /*%< 113 DNS_R_OPTERR */
+ "malformed DNSTAP data", /*%< 114 DNS_R_BADDNSTAP */
+
+ "TSIG in wrong location", /*%< 115 DNS_R_BADTSIG */
+ "SIG(0) in wrong location", /*%< 116 DNS_R_BADSIG0 */
+ "too many records", /*%< 117 DNS_R_TOOMANYRECORDS */
+};
+
+static const char *ids[DNS_R_NRESULTS] = {
+ "DNS_R_LABELTOOLONG",
+ "DNS_R_BADESCAPE",
+ /*!
+ * Note that DNS_R_BADBITSTRING and DNS_R_BITSTRINGTOOLONG are
+ * deprecated.
+ */
+ "DNS_R_BADBITSTRING",
+ "DNS_R_BITSTRINGTOOLONG",
+ "DNS_R_EMPTYLABEL",
+ "DNS_R_BADDOTTEDQUAD",
+ "DNS_R_INVALIDNS",
+ "DNS_R_UNKNOWN",
+ "DNS_R_BADLABELTYPE",
+ "DNS_R_BADPOINTER",
+ "DNS_R_TOOMANYHOPS",
+ "DNS_R_DISALLOWED",
+ "DNS_R_EXTRATOKEN",
+ "DNS_R_EXTRADATA",
+ "DNS_R_TEXTTOOLONG",
+ "DNS_R_NOTZONETOP",
+ "DNS_R_SYNTAX",
+ "DNS_R_BADCKSUM",
+ "DNS_R_BADAAAA",
+ "DNS_R_NOOWNER",
+ "DNS_R_NOTTL",
+ "DNS_R_BADCLASS",
+ "DNS_R_NAMETOOLONG",
+ "DNS_R_PARTIALMATCH",
+ "DNS_R_NEWORIGIN",
+ "DNS_R_UNCHANGED",
+ "DNS_R_BADTTL",
+ "DNS_R_NOREDATA",
+ "DNS_R_CONTINUE",
+ "DNS_R_DELEGATION",
+ "DNS_R_GLUE",
+ "DNS_R_DNAME",
+ "DNS_R_CNAME",
+ "DNS_R_BADDB",
+ "DNS_R_ZONECUT",
+ "DNS_R_BADZONE",
+ "DNS_R_MOREDATA",
+ "DNS_R_UPTODATE",
+ "DNS_R_TSIGVERIFYFAILURE",
+ "DNS_R_TSIGERRORSET",
+ "DNS_R_SIGINVALID",
+ "DNS_R_SIGEXPIRED",
+ "DNS_R_SIGFUTURE",
+ "DNS_R_KEYUNAUTHORIZED",
+ "DNS_R_INVALIDTIME",
+ "DNS_R_EXPECTEDTSIG",
+ "DNS_R_UNEXPECTEDTSIG",
+ "DNS_R_INVALIDTKEY",
+ "DNS_R_HINT",
+ "DNS_R_DROP",
+ "DNS_R_NOTLOADED",
+ "DNS_R_NCACHENXDOMAIN",
+ "DNS_R_NCACHENXRRSET",
+ "DNS_R_WAIT",
+ "DNS_R_NOTVERIFIEDYET",
+ "DNS_R_NOIDENTITY",
+ "DNS_R_NOJOURNAL",
+ "DNS_R_ALIAS",
+ "DNS_R_USETCP",
+ "DNS_R_NOVALIDSIG",
+ "DNS_R_NOVALIDNSEC",
+ "DNS_R_NOTINSECURE",
+ "DNS_R_UNKNOWNSERVICE",
+ "DNS_R_RECOVERABLE",
+ "DNS_R_UNKNOWNOPT",
+ "DNS_R_UNEXPECTEDID",
+ "DNS_R_SEENINCLUDE",
+ "DNS_R_NOTEXACT",
+ "DNS_R_BLACKHOLED",
+ "DNS_R_BADALG",
+ "DNS_R_METATYPE",
+ "DNS_R_CNAMEANDOTHER",
+ "DNS_R_SINGLETON",
+ "DNS_R_HINTNXRRSET",
+ "DNS_R_NOMASTERFILE",
+ "DNS_R_UNKNOWNPROTO",
+ "DNS_R_CLOCKSKEW",
+ "DNS_R_BADIXFR",
+ "DNS_R_NOTAUTHORITATIVE",
+ "DNS_R_NOVALIDKEY",
+ "DNS_R_OBSOLETE",
+ "DNS_R_FROZEN",
+ "DNS_R_UNKNOWNFLAG",
+ "DNS_R_EXPECTEDRESPONSE",
+ "DNS_R_NOVALIDDS",
+ "DNS_R_NSISADDRESS",
+ "DNS_R_REMOTEFORMERR",
+ "DNS_R_TRUNCATEDTCP",
+ "DNS_R_LAME",
+ "DNS_R_UNEXPECTEDRCODE",
+ "DNS_R_UNEXPECTEDOPCODE",
+ "DNS_R_CHASEDSSERVERS",
+ "DNS_R_EMPTYNAME",
+ "DNS_R_EMPTYWILD",
+ "DNS_R_BADBITMAP",
+ "DNS_R_FROMWILDCARD",
+ "DNS_R_BADOWNERNAME",
+ "DNS_R_BADNAME",
+ "DNS_R_DYNAMIC",
+ "DNS_R_UNKNOWNCOMMAND",
+ "DNS_R_MUSTBESECURE",
+ "DNS_R_COVERINGNSEC",
+ "DNS_R_MXISADDRESS",
+ "DNS_R_DUPLICATE",
+ "DNS_R_INVALIDNSEC3",
+ "DNS_R_NOTMASTER",
+ "DNS_R_BROKENCHAIN",
+ "DNS_R_EXPIRED",
+ "DNS_R_NOTDYNAMIC",
+ "DNS_R_BADEUI",
+ "DNS_R_NTACOVERED",
+ "DNS_R_BADCDS",
+ "DNS_R_BADCDNSKEY",
+ "DNS_R_OPTERR",
+ "DNS_R_BADDNSTAP",
+ "DNS_R_BADTSIG",
+ "DNS_R_BADSIG0",
+};
+
+static const char *rcode_text[DNS_R_NRCODERESULTS] = {
+ "NOERROR", /*%< 0 DNS_R_NOERROR */
+ "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 DNS_R_RCODE11 */
+ "<rcode 12>", /*%< 12 DNS_R_RCODE12 */
+ "<rcode 13>", /*%< 13 DNS_R_RCODE13 */
+ "<rcode 14>", /*%< 14 DNS_R_RCODE14 */
+
+ "<rcode 15>", /*%< 15 DNS_R_RCODE15 */
+ "BADVERS", /*%< 16 DNS_R_BADVERS */
+};
+
+static const char *rcode_ids[DNS_R_NRCODERESULTS] = {
+ "DNS_R_NOERROR",
+ "DNS_R_FORMERR",
+ "DNS_R_SERVFAIL",
+ "DNS_R_NXDOMAIN",
+ "DNS_R_NOTIMP",
+ "DNS_R_REFUSED",
+ "DNS_R_YXDOMAIN",
+ "DNS_R_YXRRSET",
+ "DNS_R_NXRRSET",
+ "DNS_R_NOTAUTH",
+ "DNS_R_NOTZONE",
+ "DNS_R_RCODE11",
+ "RNS_R_RCODE12",
+ "DNS_R_RCODE13",
+ "DNS_R_RCODE14",
+ "DNS_R_RCODE15",
+ "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);
+
+ result = isc_result_registerids(ISC_RESULTCLASS_DNS, DNS_R_NRESULTS,
+ ids, dns_msgcat, DNS_RESULT_RESULTSET);
+ if (result == ISC_R_SUCCESS)
+ result = isc_result_registerids(ISC_RESULTCLASS_DNSRCODE,
+ DNS_R_NRCODERESULTS,
+ rcode_ids, dns_msgcat,
+ DNS_RESULT_RCODERESULTSET);
+ if (result != ISC_R_SUCCESS)
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "isc_result_registerids() 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:
+ case DNS_R_NAMETOOLONG:
+ case DNS_R_OPTERR:
+ 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..70b2b7d
--- /dev/null
+++ b/lib/dns/rootns.c
@@ -0,0 +1,528 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdbool.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 199.9.14.201\n"
+"B.ROOT-SERVERS.NET. 3600000 IN AAAA 2001:500:200::b\n"
+"C.ROOT-SERVERS.NET. 3600000 IN A 192.33.4.12\n"
+"C.ROOT-SERVERS.NET. 3600000 IN AAAA 2001:500:2::c\n"
+"D.ROOT-SERVERS.NET. 3600000 IN A 199.7.91.13\n"
+"D.ROOT-SERVERS.NET. 3600000 IN AAAA 2001:500:2d::d\n"
+"E.ROOT-SERVERS.NET. 3600000 IN A 192.203.230.10\n"
+"E.ROOT-SERVERS.NET. 3600000 IN AAAA 2001:500:a8::e\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"
+"G.ROOT-SERVERS.NET. 3600000 IN AAAA 2001:500:12::d0d\n"
+"H.ROOT-SERVERS.NET. 3600000 IN A 198.97.190.53\n"
+"H.ROOT-SERVERS.NET. 3600000 IN AAAA 2001:500:1::53\n"
+"I.ROOT-SERVERS.NET. 3600000 IN A 192.36.148.17\n"
+"I.ROOT-SERVERS.NET. 3600000 IN AAAA 2001:7fe::53\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"
+"L.ROOT-SERVERS.NET. 3600000 IN AAAA 2001:500:9f::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);
+
+ name = dns_fixedname_initname(&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;
+ unsigned int 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)
+ goto failure;
+
+ len = strlen(root_ns);
+ isc_buffer_init(&source, root_ns, len);
+ isc_buffer_add(&source, len);
+
+ dns_rdatacallbacks_init(&callbacks);
+ result = dns_db_beginload(db, &callbacks);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+ 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);
+ if (result == ISC_R_SUCCESS || result == DNS_R_SEENINCLUDE)
+ result = eresult;
+ if (result != ISC_R_SUCCESS && result != DNS_R_SEENINCLUDE)
+ goto failure;
+ 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);
+
+ failure:
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_HINTS,
+ ISC_LOG_ERROR, "could not configure root hints from "
+ "'%s': %s", (filename != NULL) ? filename : "<BUILT-IN>",
+ isc_result_totext(result));
+
+ if (db != NULL)
+ dns_db_detach(&db);
+
+ return (result);
+}
+
+static void
+report(dns_view_t *view, dns_name_t *name, bool 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 bool
+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 (true);
+ dns_rdata_reset(&current);
+ result = dns_rdataset_next(rrset);
+ }
+ return (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);
+ foundname = dns_fixedname_initname(&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, 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, 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, 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, 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, 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, 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);
+ name = dns_fixedname_initname(&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/rpz.c b/lib/dns/rpz.c
new file mode 100644
index 0000000..f0993be
--- /dev/null
+++ b/lib/dns/rpz.c
@@ -0,0 +1,2413 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/buffer.h>
+#include <isc/mem.h>
+#include <isc/net.h>
+#include <isc/netaddr.h>
+#include <isc/print.h>
+#include <isc/rwlock.h>
+#include <isc/stdlib.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dns/db.h>
+#include <dns/fixedname.h>
+#include <dns/log.h>
+#include <dns/rdata.h>
+#include <dns/rdataset.h>
+#include <dns/rdatastruct.h>
+#include <dns/result.h>
+#include <dns/rbt.h>
+#include <dns/rpz.h>
+#include <dns/view.h>
+
+
+/*
+ * Parallel radix trees for databases of response policy IP addresses
+ *
+ * The radix or patricia trees are somewhat specialized to handle response
+ * policy addresses by representing the two sets of IP addresses and name
+ * server IP addresses in a single tree. One set of IP addresses is
+ * for rpz-ip policies or policies triggered by addresses in A or
+ * AAAA records in responses.
+ * The second set is for rpz-nsip policies or policies triggered by addresses
+ * in A or AAAA records for NS records that are authorities for responses.
+ *
+ * Each leaf indicates that an IP address is listed in the IP address or the
+ * name server IP address policy sub-zone (or both) of the corresponding
+ * response policy zone. The policy data such as a CNAME or an A record
+ * is kept in the policy zone. After an IP address has been found in a radix
+ * tree, the node in the policy zone's database is found by converting
+ * the IP address to a domain name in a canonical form.
+ *
+ *
+ * The response policy zone canonical form of an IPv6 address is one of:
+ * prefix.W.W.W.W.W.W.W.W
+ * prefix.WORDS.zz
+ * prefix.WORDS.zz.WORDS
+ * prefix.zz.WORDS
+ * where
+ * prefix is the prefix length of the IPv6 address between 1 and 128
+ * W is a number between 0 and 65535
+ * WORDS is one or more numbers W separated with "."
+ * zz corresponds to :: in the standard IPv6 text representation
+ *
+ * The canonical form of IPv4 addresses is:
+ * prefix.B.B.B.B
+ * where
+ * prefix is the prefix length of the address between 1 and 32
+ * B is a number between 0 and 255
+ *
+ * Names for IPv4 addresses are distinguished from IPv6 addresses by having
+ * 5 labels all of which are numbers, and a prefix between 1 and 32.
+ */
+
+
+/*
+ * Use a private definition of IPv6 addresses because s6_addr32 is not
+ * always defined and our IPv6 addresses are in non-standard byte order
+ */
+typedef uint32_t dns_rpz_cidr_word_t;
+#define DNS_RPZ_CIDR_WORD_BITS ((int)sizeof(dns_rpz_cidr_word_t)*8)
+#define DNS_RPZ_CIDR_KEY_BITS ((int)sizeof(dns_rpz_cidr_key_t)*8)
+#define DNS_RPZ_CIDR_WORDS (128/DNS_RPZ_CIDR_WORD_BITS)
+typedef struct {
+ dns_rpz_cidr_word_t w[DNS_RPZ_CIDR_WORDS];
+} dns_rpz_cidr_key_t;
+
+#define ADDR_V4MAPPED 0xffff
+#define KEY_IS_IPV4(prefix,ip) ((prefix) >= 96 && (ip)->w[0] == 0 && \
+ (ip)->w[1] == 0 && (ip)->w[2] == ADDR_V4MAPPED)
+
+#define DNS_RPZ_WORD_MASK(b) ((b) == 0 ? (dns_rpz_cidr_word_t)(-1) \
+ : ((dns_rpz_cidr_word_t)(-1) \
+ << (DNS_RPZ_CIDR_WORD_BITS - (b))))
+
+/*
+ * Get bit #n from the array of words of an IP address.
+ */
+#define DNS_RPZ_IP_BIT(ip, n) (1 & ((ip)->w[(n)/DNS_RPZ_CIDR_WORD_BITS] >> \
+ (DNS_RPZ_CIDR_WORD_BITS \
+ - 1 - ((n) % DNS_RPZ_CIDR_WORD_BITS))))
+
+/*
+ * A triplet of arrays of bits flagging the existence of
+ * client-IP, IP, and NSIP policy triggers.
+ */
+typedef struct dns_rpz_addr_zbits dns_rpz_addr_zbits_t;
+struct dns_rpz_addr_zbits {
+ dns_rpz_zbits_t client_ip;
+ dns_rpz_zbits_t ip;
+ dns_rpz_zbits_t nsip;
+};
+
+/*
+ * A CIDR or radix tree node.
+ */
+struct dns_rpz_cidr_node {
+ dns_rpz_cidr_node_t *parent;
+ dns_rpz_cidr_node_t *child[2];
+ dns_rpz_cidr_key_t ip;
+ dns_rpz_prefix_t prefix;
+ dns_rpz_addr_zbits_t set;
+ dns_rpz_addr_zbits_t sum;
+};
+
+/*
+ * A pair of arrays of bits flagging the existence of
+ * QNAME and NSDNAME policy triggers.
+ */
+typedef struct dns_rpz_nm_zbits dns_rpz_nm_zbits_t;
+struct dns_rpz_nm_zbits {
+ dns_rpz_zbits_t qname;
+ dns_rpz_zbits_t ns;
+};
+
+/*
+ * The data in a RBT node has two pairs of bits for policy zones.
+ * One pair is for the corresponding name of the node such as example.com
+ * and the other pair is for a wildcard child such as *.example.com.
+ */
+typedef struct dns_rpz_nm_data dns_rpz_nm_data_t;
+struct dns_rpz_nm_data {
+ dns_rpz_nm_zbits_t set;
+ dns_rpz_nm_zbits_t wild;
+};
+
+#if 0
+/*
+ * Catch a name while debugging.
+ */
+static void
+catch_name(const dns_name_t *src_name, const char *tgt, const char *str) {
+ dns_fixedname_t tgt_namef;
+ dns_name_t *tgt_name;
+
+ tgt_name = dns_fixedname_initname(&tgt_namef);
+ dns_name_fromstring(tgt_name, tgt, DNS_NAME_DOWNCASE, NULL);
+ if (dns_name_equal(src_name, tgt_name)) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+ DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL,
+ "rpz hit failed: %s %s", str, tgt);
+ }
+}
+#endif
+
+const char *
+dns_rpz_type2str(dns_rpz_type_t type) {
+ switch (type) {
+ case DNS_RPZ_TYPE_CLIENT_IP:
+ return ("CLIENT-IP");
+ case DNS_RPZ_TYPE_QNAME:
+ return ("QNAME");
+ case DNS_RPZ_TYPE_IP:
+ return ("IP");
+ case DNS_RPZ_TYPE_NSIP:
+ return ("NSIP");
+ case DNS_RPZ_TYPE_NSDNAME:
+ return ("NSDNAME");
+ case DNS_RPZ_TYPE_BAD:
+ break;
+ }
+ FATAL_ERROR(__FILE__, __LINE__, "impossible rpz type %d", type);
+ return ("impossible");
+}
+
+dns_rpz_policy_t
+dns_rpz_str2policy(const char *str) {
+ static struct {
+ const char *str;
+ dns_rpz_policy_t policy;
+ } tbl[] = {
+ {"given", DNS_RPZ_POLICY_GIVEN},
+ {"disabled", DNS_RPZ_POLICY_DISABLED},
+ {"passthru", DNS_RPZ_POLICY_PASSTHRU},
+ {"drop", DNS_RPZ_POLICY_DROP},
+ {"tcp-only", DNS_RPZ_POLICY_TCP_ONLY},
+ {"nxdomain", DNS_RPZ_POLICY_NXDOMAIN},
+ {"nodata", DNS_RPZ_POLICY_NODATA},
+ {"cname", DNS_RPZ_POLICY_CNAME},
+ {"no-op", DNS_RPZ_POLICY_PASSTHRU}, /* old passthru */
+ };
+ unsigned int n;
+
+ if (str == NULL)
+ return (DNS_RPZ_POLICY_ERROR);
+ for (n = 0; n < sizeof(tbl)/sizeof(tbl[0]); ++n) {
+ if (!strcasecmp(tbl[n].str, str))
+ return (tbl[n].policy);
+ }
+ return (DNS_RPZ_POLICY_ERROR);
+}
+
+const char *
+dns_rpz_policy2str(dns_rpz_policy_t policy) {
+ const char *str;
+
+ switch (policy) {
+ case DNS_RPZ_POLICY_PASSTHRU:
+ str = "PASSTHRU";
+ break;
+ case DNS_RPZ_POLICY_DROP:
+ str = "DROP";
+ break;
+ case DNS_RPZ_POLICY_TCP_ONLY:
+ str = "TCP-ONLY";
+ break;
+ case DNS_RPZ_POLICY_NXDOMAIN:
+ str = "NXDOMAIN";
+ break;
+ case DNS_RPZ_POLICY_NODATA:
+ str = "NODATA";
+ break;
+ case DNS_RPZ_POLICY_RECORD:
+ str = "Local-Data";
+ break;
+ case DNS_RPZ_POLICY_CNAME:
+ case DNS_RPZ_POLICY_WILDCNAME:
+ str = "CNAME";
+ break;
+ case DNS_RPZ_POLICY_MISS:
+ str = "MISS";
+ break;
+ default:
+ str = "";
+ POST(str);
+ INSIST(0);
+ }
+ return (str);
+}
+
+/*
+ * Return the bit number of the highest set bit in 'zbit'.
+ * (for example, 0x01 returns 0, 0xFF returns 7, etc.)
+ */
+static int
+zbit_to_num(dns_rpz_zbits_t zbit) {
+ dns_rpz_num_t rpz_num;
+
+ REQUIRE(zbit != 0);
+ rpz_num = 0;
+#if DNS_RPZ_MAX_ZONES > 32
+ if ((zbit & 0xffffffff00000000L) != 0) {
+ zbit >>= 32;
+ rpz_num += 32;
+ }
+#endif
+ if ((zbit & 0xffff0000) != 0) {
+ zbit >>= 16;
+ rpz_num += 16;
+ }
+ if ((zbit & 0xff00) != 0) {
+ zbit >>= 8;
+ rpz_num += 8;
+ }
+ if ((zbit & 0xf0) != 0) {
+ zbit >>= 4;
+ rpz_num += 4;
+ }
+ if ((zbit & 0xc) != 0) {
+ zbit >>= 2;
+ rpz_num += 2;
+ }
+ if ((zbit & 2) != 0)
+ ++rpz_num;
+ return (rpz_num);
+}
+
+/*
+ * Make a set of bit masks given one or more bits and their type.
+ */
+static void
+make_addr_set(dns_rpz_addr_zbits_t *tgt_set, dns_rpz_zbits_t zbits,
+ dns_rpz_type_t type)
+{
+ switch (type) {
+ case DNS_RPZ_TYPE_CLIENT_IP:
+ tgt_set->client_ip = zbits;
+ tgt_set->ip = 0;
+ tgt_set->nsip = 0;
+ break;
+ case DNS_RPZ_TYPE_IP:
+ tgt_set->client_ip = 0;
+ tgt_set->ip = zbits;
+ tgt_set->nsip = 0;
+ break;
+ case DNS_RPZ_TYPE_NSIP:
+ tgt_set->client_ip = 0;
+ tgt_set->ip = 0;
+ tgt_set->nsip = zbits;
+ break;
+ default:
+ INSIST(0);
+ break;
+ }
+}
+
+static void
+make_nm_set(dns_rpz_nm_zbits_t *tgt_set,
+ dns_rpz_num_t rpz_num, dns_rpz_type_t type)
+{
+ switch (type) {
+ case DNS_RPZ_TYPE_QNAME:
+ tgt_set->qname = DNS_RPZ_ZBIT(rpz_num);
+ tgt_set->ns = 0;
+ break;
+ case DNS_RPZ_TYPE_NSDNAME:
+ tgt_set->qname = 0;
+ tgt_set->ns = DNS_RPZ_ZBIT(rpz_num);
+ break;
+ default:
+ INSIST(0);
+ break;
+ }
+}
+
+/*
+ * Mark a node and all of its parents as having client-IP, IP, or NSIP data
+ */
+static void
+set_sum_pair(dns_rpz_cidr_node_t *cnode) {
+ dns_rpz_cidr_node_t *child;
+ dns_rpz_addr_zbits_t sum;
+
+ do {
+ sum = cnode->set;
+
+ child = cnode->child[0];
+ if (child != NULL) {
+ sum.client_ip |= child->sum.client_ip;
+ sum.ip |= child->sum.ip;
+ sum.nsip |= child->sum.nsip;
+ }
+
+ child = cnode->child[1];
+ if (child != NULL) {
+ sum.client_ip |= child->sum.client_ip;
+ sum.ip |= child->sum.ip;
+ sum.nsip |= child->sum.nsip;
+ }
+
+ if (cnode->sum.client_ip == sum.client_ip &&
+ cnode->sum.ip == sum.ip &&
+ cnode->sum.nsip == sum.nsip)
+ break;
+ cnode->sum = sum;
+ cnode = cnode->parent;
+ } while (cnode != NULL);
+}
+
+/* Caller must hold rpzs->maint_lock */
+static void
+fix_qname_skip_recurse(dns_rpz_zones_t *rpzs) {
+ dns_rpz_zbits_t mask;
+
+ /*
+ * qname_wait_recurse and qname_skip_recurse are used to
+ * implement the "qname-wait-recurse" config option.
+ *
+ * When "qname-wait-recurse" is yes, no processing happens
+ * without recursion. In this case, qname_wait_recurse is true,
+ * and qname_skip_recurse (a bitfield indicating which policy
+ * zones can be processed without recursion) is set to all 0's
+ * by fix_qname_skip_recurse().
+ *
+ * When "qname-wait-recurse" is no, qname_skip_recurse may be
+ * set to a non-zero value by fix_qname_skip_recurse(). The mask
+ * has to have bits set for the policy zones for which
+ * processing may continue without recursion, and bits cleared
+ * for the rest.
+ *
+ * (1) The ARM says:
+ *
+ * The "qname-wait-recurse no" option overrides that default
+ * behavior when recursion cannot change a non-error
+ * response. The option does not affect QNAME or client-IP
+ * triggers in policy zones listed after other zones
+ * containing IP, NSIP and NSDNAME triggers, because those may
+ * depend on the A, AAAA, and NS records that would be found
+ * during recursive resolution.
+ *
+ * Let's consider the following:
+ *
+ * zbits_req = (rpzs->have.ipv4 | rpzs->have.ipv6 |
+ * rpzs->have.nsdname |
+ * rpzs->have.nsipv4 | rpzs->have.nsipv6);
+ *
+ * zbits_req now contains bits set for zones which require
+ * recursion.
+ *
+ * But going by the description in the ARM, if the first policy
+ * zone requires recursion, then all zones after that (higher
+ * order bits) have to wait as well. If the Nth zone requires
+ * recursion, then (N+1)th zone onwards all need to wait.
+ *
+ * So mapping this, examples:
+ *
+ * zbits_req = 0b000 mask = 0xffffffff (no zones have to wait for
+ * recursion)
+ * zbits_req = 0b001 mask = 0x00000000 (all zones have to wait)
+ * zbits_req = 0b010 mask = 0x00000001 (the first zone doesn't have to
+ * wait, second zone onwards need
+ * to wait)
+ * zbits_req = 0b011 mask = 0x00000000 (all zones have to wait)
+ * zbits_req = 0b100 mask = 0x00000011 (the 1st and 2nd zones don't
+ * have to wait, third zone
+ * onwards need to wait)
+ *
+ * More generally, we have to count the number of trailing 0
+ * bits in zbits_req and only these can be processed without
+ * recursion. All the rest need to wait.
+ *
+ * (2) The ARM says that "qname-wait-recurse no" option
+ * overrides the default behavior when recursion cannot change a
+ * non-error response. So, in the order of listing of policy
+ * zones, within the first policy zone where recursion may be
+ * required, we should first allow CLIENT-IP and QNAME policy
+ * records to be attempted without recursion.
+ */
+
+ /*
+ * Get a mask covering all policy zones that are not subordinate to
+ * other policy zones containing triggers that require that the
+ * qname be resolved before they can be checked.
+ */
+ rpzs->have.client_ip = rpzs->have.client_ipv4 | rpzs->have.client_ipv6;
+ rpzs->have.ip = rpzs->have.ipv4 | rpzs->have.ipv6;
+ rpzs->have.nsip = rpzs->have.nsipv4 | rpzs->have.nsipv6;
+
+ if (rpzs->p.qname_wait_recurse) {
+ mask = 0;
+ } else {
+ dns_rpz_zbits_t zbits_req;
+ dns_rpz_zbits_t zbits_notreq;
+ dns_rpz_zbits_t mask2;
+ dns_rpz_zbits_t req_mask;
+
+ /*
+ * Get the masks of zones with policies that
+ * do/don't require recursion
+ */
+
+ zbits_req = (rpzs->have.ipv4 | rpzs->have.ipv6 |
+ rpzs->have.nsdname |
+ rpzs->have.nsipv4 | rpzs->have.nsipv6);
+ zbits_notreq = (rpzs->have.client_ip | rpzs->have.qname);
+
+ if (zbits_req == 0) {
+ mask = DNS_RPZ_ALL_ZBITS;
+ goto set;
+ }
+
+ /*
+ * req_mask is a mask covering used bits in
+ * zbits_req. (For instance, 0b1 => 0b1, 0b101 => 0b111,
+ * 0b11010101 => 0b11111111).
+ */
+ req_mask = zbits_req;
+ req_mask |= req_mask >> 1;
+ req_mask |= req_mask >> 2;
+ req_mask |= req_mask >> 4;
+ req_mask |= req_mask >> 8;
+ req_mask |= req_mask >> 16;
+#if DNS_RPZ_MAX_ZONES > 32
+ req_mask |= req_mask >> 32;
+#endif
+
+ /*
+ * There's no point in skipping recursion for a later
+ * zone if it is required in a previous zone.
+ */
+ if ((zbits_notreq & req_mask) == 0) {
+ mask = 0;
+ goto set;
+ }
+
+ /*
+ * This bit arithmetic creates a mask of zones in which
+ * it is okay to skip recursion. After the first zone
+ * that has to wait for recursion, all the others have
+ * to wait as well, so we want to create a mask in which
+ * all the trailing zeroes in zbits_req are are 1, and
+ * more significant bits are 0. (For instance,
+ * 0x0700 => 0x00ff, 0x0007 => 0x0000)
+ */
+ mask = ~(zbits_req | ((~zbits_req) + 1));
+
+ /*
+ * As mentioned in (2) above, the zone corresponding to
+ * the least significant zero could have its CLIENT-IP
+ * and QNAME policies checked before recursion, if it
+ * has any of those policies. So if it does, we
+ * can set its 0 to 1.
+ *
+ * Locate the least significant 0 bit in the mask (for
+ * instance, 0xff => 0x100)...
+ */
+ mask2 = (mask << 1) & ~mask;
+
+ /*
+ * Also set the bit for zone 0, because if it's in
+ * zbits_notreq then it's definitely okay to attempt to
+ * skip recursion for zone 0...
+ */
+ mask2 |= 1;
+
+ /* Clear any bits *not* in zbits_notreq... */
+ mask2 &= zbits_notreq;
+
+ /* And merge the result into the skip-recursion mask */
+ mask |= mask2;
+ }
+
+ set:
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ, DNS_LOGMODULE_RBTDB,
+ DNS_RPZ_DEBUG_QUIET,
+ "computed RPZ qname_skip_recurse mask=0x%" PRIx64,
+ (uint64_t) mask);
+ rpzs->have.qname_skip_recurse = mask;
+}
+
+static void
+adj_trigger_cnt(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
+ dns_rpz_type_t rpz_type,
+ const dns_rpz_cidr_key_t *tgt_ip, dns_rpz_prefix_t tgt_prefix,
+ bool inc)
+{
+ dns_rpz_trigger_counter_t *cnt;
+ dns_rpz_zbits_t *have;
+
+ switch (rpz_type) {
+ case DNS_RPZ_TYPE_CLIENT_IP:
+ REQUIRE(tgt_ip != NULL);
+ if (KEY_IS_IPV4(tgt_prefix, tgt_ip)) {
+ cnt = &rpzs->triggers[rpz_num].client_ipv4;
+ have = &rpzs->have.client_ipv4;
+ } else {
+ cnt = &rpzs->triggers[rpz_num].client_ipv6;
+ have = &rpzs->have.client_ipv6;
+ }
+ break;
+ case DNS_RPZ_TYPE_QNAME:
+ cnt = &rpzs->triggers[rpz_num].qname;
+ have = &rpzs->have.qname;
+ break;
+ case DNS_RPZ_TYPE_IP:
+ REQUIRE(tgt_ip != NULL);
+ if (KEY_IS_IPV4(tgt_prefix, tgt_ip)) {
+ cnt = &rpzs->triggers[rpz_num].ipv4;
+ have = &rpzs->have.ipv4;
+ } else {
+ cnt = &rpzs->triggers[rpz_num].ipv6;
+ have = &rpzs->have.ipv6;
+ }
+ break;
+ case DNS_RPZ_TYPE_NSDNAME:
+ cnt = &rpzs->triggers[rpz_num].nsdname;
+ have = &rpzs->have.nsdname;
+ break;
+ case DNS_RPZ_TYPE_NSIP:
+ REQUIRE(tgt_ip != NULL);
+ if (KEY_IS_IPV4(tgt_prefix, tgt_ip)) {
+ cnt = &rpzs->triggers[rpz_num].nsipv4;
+ have = &rpzs->have.nsipv4;
+ } else {
+ cnt = &rpzs->triggers[rpz_num].nsipv6;
+ have = &rpzs->have.nsipv6;
+ }
+ break;
+ default:
+ INSIST(0);
+ }
+
+ if (inc) {
+ if (++*cnt == 1U) {
+ *have |= DNS_RPZ_ZBIT(rpz_num);
+ fix_qname_skip_recurse(rpzs);
+ }
+ } else {
+ REQUIRE(*cnt != 0U);
+ if (--*cnt == 0U) {
+ *have &= ~DNS_RPZ_ZBIT(rpz_num);
+ fix_qname_skip_recurse(rpzs);
+ }
+ }
+}
+
+static dns_rpz_cidr_node_t *
+new_node(dns_rpz_zones_t *rpzs,
+ const dns_rpz_cidr_key_t *ip, dns_rpz_prefix_t prefix,
+ const dns_rpz_cidr_node_t *child)
+{
+ dns_rpz_cidr_node_t *node;
+ int i, words, wlen;
+
+ node = isc_mem_get(rpzs->mctx, sizeof(*node));
+ if (node == NULL)
+ return (NULL);
+ memset(node, 0, sizeof(*node));
+
+ if (child != NULL)
+ node->sum = child->sum;
+
+ node->prefix = prefix;
+ words = prefix / DNS_RPZ_CIDR_WORD_BITS;
+ wlen = prefix % DNS_RPZ_CIDR_WORD_BITS;
+ i = 0;
+ while (i < words) {
+ node->ip.w[i] = ip->w[i];
+ ++i;
+ }
+ if (wlen != 0) {
+ node->ip.w[i] = ip->w[i] & DNS_RPZ_WORD_MASK(wlen);
+ ++i;
+ }
+ while (i < DNS_RPZ_CIDR_WORDS)
+ node->ip.w[i++] = 0;
+
+ return (node);
+}
+
+static void
+badname(int level, dns_name_t *name, const char *str1, const char *str2) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+
+ /*
+ * bin/tests/system/rpz/tests.sh looks for "invalid rpz".
+ */
+ if (level < DNS_RPZ_DEBUG_QUIET &&
+ isc_log_wouldlog(dns_lctx, level)) {
+ dns_name_format(name, namebuf, sizeof(namebuf));
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+ DNS_LOGMODULE_RBTDB, level,
+ "invalid rpz IP address \"%s\"%s%s",
+ namebuf, str1, str2);
+ }
+}
+
+/*
+ * Convert an IP address from radix tree binary (host byte order) to
+ * to its canonical response policy domain name without the origin of the
+ * policy zone.
+ *
+ * Generate a name for an IPv6 address that fits RFC 5952, except that
+ * our reversed format requires that when the length of the consecutive
+ * 16-bit 0 fields are equal (e.g., 1.0.0.1.0.0.db8.2001 corresponding
+ * to 2001:db8:0:0:1:0:0:1), we shorted the last instead of the first
+ * (e.g., 1.0.0.1.zz.db8.2001 corresponding to 2001:db8::1:0:0:1).
+ */
+static isc_result_t
+ip2name(const dns_rpz_cidr_key_t *tgt_ip, dns_rpz_prefix_t tgt_prefix,
+ dns_name_t *base_name, dns_name_t *ip_name)
+{
+#ifndef INET6_ADDRSTRLEN
+#define INET6_ADDRSTRLEN 46
+#endif
+ int w[DNS_RPZ_CIDR_WORDS*2];
+ char str[1+8+1+INET6_ADDRSTRLEN+1];
+ isc_buffer_t buffer;
+ isc_result_t result;
+ int best_first, best_len, cur_first, cur_len;
+ int i, n, len;
+
+ if (KEY_IS_IPV4(tgt_prefix, tgt_ip)) {
+ len = snprintf(str, sizeof(str), "%u.%u.%u.%u.%u",
+ tgt_prefix - 96U,
+ tgt_ip->w[3] & 0xffU,
+ (tgt_ip->w[3]>>8) & 0xffU,
+ (tgt_ip->w[3]>>16) & 0xffU,
+ (tgt_ip->w[3]>>24) & 0xffU);
+ if (len < 0 || len > (int)sizeof(str)) {
+ return (ISC_R_FAILURE);
+ }
+ } else {
+ len = snprintf(str, sizeof(str), "%d", tgt_prefix);
+ if (len == -1)
+ return (ISC_R_FAILURE);
+ for (i = 0; i < DNS_RPZ_CIDR_WORDS; i++) {
+ w[i*2+1] = ((tgt_ip->w[DNS_RPZ_CIDR_WORDS-1-i] >> 16)
+ & 0xffff);
+ w[i*2] = tgt_ip->w[DNS_RPZ_CIDR_WORDS-1-i] & 0xffff;
+ }
+ /*
+ * Find the start and length of the first longest sequence
+ * of zeros in the address.
+ */
+ best_first = -1;
+ best_len = 0;
+ cur_first = -1;
+ cur_len = 0;
+ for (n = 0; n <=7; ++n) {
+ if (w[n] != 0) {
+ cur_len = 0;
+ cur_first = -1;
+ } else {
+ ++cur_len;
+ if (cur_first < 0) {
+ cur_first = n;
+ } else if (cur_len >= best_len) {
+ best_first = cur_first;
+ best_len = cur_len;
+ }
+ }
+ }
+
+ for (n = 0; n <= 7; ++n) {
+ INSIST(len < (int)sizeof(str));
+ if (n == best_first) {
+ len += snprintf(str + len, sizeof(str) - len,
+ ".zz");
+ n += best_len - 1;
+ } else {
+ len += snprintf(str + len, sizeof(str) - len,
+ ".%x", w[n]);
+ }
+ }
+ }
+
+ isc_buffer_init(&buffer, str, sizeof(str));
+ isc_buffer_add(&buffer, len);
+ result = dns_name_fromtext(ip_name, &buffer, base_name, 0, NULL);
+ return (result);
+}
+
+/*
+ * Determine the type of a name in a response policy zone.
+ */
+static dns_rpz_type_t
+type_from_name(dns_rpz_zone_t *rpz, dns_name_t *name) {
+
+ if (dns_name_issubdomain(name, &rpz->ip))
+ return (DNS_RPZ_TYPE_IP);
+
+ if (dns_name_issubdomain(name, &rpz->client_ip))
+ return (DNS_RPZ_TYPE_CLIENT_IP);
+
+#ifdef ENABLE_RPZ_NSIP
+ if (dns_name_issubdomain(name, &rpz->nsip))
+ return (DNS_RPZ_TYPE_NSIP);
+#endif
+
+#ifdef ENABLE_RPZ_NSDNAME
+ if (dns_name_issubdomain(name, &rpz->nsdname))
+ return (DNS_RPZ_TYPE_NSDNAME);
+#endif
+
+ return (DNS_RPZ_TYPE_QNAME);
+}
+
+/*
+ * Convert an IP address from canonical response policy domain name form
+ * to radix tree binary (host byte order) for adding or deleting IP or NSIP
+ * data.
+ */
+static isc_result_t
+name2ipkey(int log_level,
+ const dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
+ dns_rpz_type_t rpz_type, dns_name_t *src_name,
+ dns_rpz_cidr_key_t *tgt_ip, dns_rpz_prefix_t *tgt_prefix,
+ dns_rpz_addr_zbits_t *new_set)
+{
+ dns_rpz_zone_t *rpz;
+ char ip_str[DNS_NAME_FORMATSIZE];
+ char ip2_str[DNS_NAME_FORMATSIZE];
+ dns_offsets_t ip_name_offsets;
+ dns_fixedname_t ip_name2f;
+ dns_name_t ip_name, *ip_name2;
+ const char *prefix_str, *cp, *end;
+ char *cp2;
+ int ip_labels;
+ dns_rpz_prefix_t prefix;
+ unsigned long prefix_num, l;
+ isc_result_t result;
+ int i;
+
+ REQUIRE(rpzs != NULL && rpz_num < rpzs->p.num_zones);
+ rpz = rpzs->zones[rpz_num];
+ REQUIRE(rpz != NULL);
+
+ make_addr_set(new_set, DNS_RPZ_ZBIT(rpz_num), rpz_type);
+
+ ip_labels = dns_name_countlabels(src_name);
+ if (rpz_type == DNS_RPZ_TYPE_QNAME)
+ ip_labels -= dns_name_countlabels(&rpz->origin);
+ else
+ ip_labels -= dns_name_countlabels(&rpz->nsdname);
+ if (ip_labels < 2) {
+ badname(log_level, src_name, "; too short", "");
+ return (ISC_R_FAILURE);
+ }
+ dns_name_init(&ip_name, ip_name_offsets);
+ dns_name_getlabelsequence(src_name, 0, ip_labels, &ip_name);
+
+ /*
+ * Get text for the IP address
+ */
+ dns_name_format(&ip_name, ip_str, sizeof(ip_str));
+ end = &ip_str[strlen(ip_str)+1];
+ prefix_str = ip_str;
+
+ prefix_num = strtoul(prefix_str, &cp2, 10);
+ if (*cp2 != '.') {
+ badname(log_level, src_name,
+ "; invalid leading prefix length", "");
+ return (ISC_R_FAILURE);
+ }
+
+ if (prefix_num < 1U || prefix_num > 128U) {
+ badname(log_level, src_name,
+ "; invalid prefix length of ", prefix_str);
+ return (ISC_R_FAILURE);
+ }
+ cp = cp2+1;
+
+ if (--ip_labels == 4 && !strchr(cp, 'z')) {
+ /*
+ * Convert an IPv4 address
+ * from the form "prefix.z.y.x.w"
+ */
+ if (prefix_num > 32U) {
+ badname(log_level, src_name,
+ "; invalid IPv4 prefix length of ", prefix_str);
+ return (ISC_R_FAILURE);
+ }
+ prefix_num += 96;
+ *tgt_prefix = (dns_rpz_prefix_t)prefix_num;
+ tgt_ip->w[0] = 0;
+ tgt_ip->w[1] = 0;
+ tgt_ip->w[2] = ADDR_V4MAPPED;
+ tgt_ip->w[3] = 0;
+ for (i = 0; i < 32; i += 8) {
+ l = strtoul(cp, &cp2, 10);
+ if (l > 255U || (*cp2 != '.' && *cp2 != '\0')) {
+ if (*cp2 == '.')
+ *cp2 = '\0';
+ badname(log_level, src_name,
+ "; invalid IPv4 octet ", cp);
+ return (ISC_R_FAILURE);
+ }
+ tgt_ip->w[3] |= l << i;
+ cp = cp2 + 1;
+ }
+ } else {
+ /*
+ * Convert a text IPv6 address.
+ */
+ *tgt_prefix = (dns_rpz_prefix_t)prefix_num;
+ for (i = 0;
+ ip_labels > 0 && i < DNS_RPZ_CIDR_WORDS * 2;
+ ip_labels--) {
+ if (cp[0] == 'z' && cp[1] == 'z' &&
+ (cp[2] == '.' || cp[2] == '\0') &&
+ i <= 6) {
+ do {
+ if ((i & 1) == 0)
+ tgt_ip->w[3-i/2] = 0;
+ ++i;
+ } while (ip_labels + i <= 8);
+ cp += 3;
+ } else {
+ l = strtoul(cp, &cp2, 16);
+ if (l > 0xffffu ||
+ (*cp2 != '.' && *cp2 != '\0')) {
+ if (*cp2 == '.')
+ *cp2 = '\0';
+ badname(log_level, src_name,
+ "; invalid IPv6 word ", cp);
+ return (ISC_R_FAILURE);
+ }
+ if ((i & 1) == 0)
+ tgt_ip->w[3-i/2] = l;
+ else
+ tgt_ip->w[3-i/2] |= l << 16;
+ i++;
+ cp = cp2 + 1;
+ }
+ }
+ }
+ if (cp != end) {
+ badname(log_level, src_name, "", "");
+ return (ISC_R_FAILURE);
+ }
+
+ /*
+ * Check for 1s after the prefix length.
+ */
+ prefix = (dns_rpz_prefix_t)prefix_num;
+ while (prefix < DNS_RPZ_CIDR_KEY_BITS) {
+ dns_rpz_cidr_word_t aword;
+
+ i = prefix % DNS_RPZ_CIDR_WORD_BITS;
+ aword = tgt_ip->w[prefix / DNS_RPZ_CIDR_WORD_BITS];
+ if ((aword & ~DNS_RPZ_WORD_MASK(i)) != 0) {
+ badname(log_level, src_name,
+ "; too small prefix length of ", prefix_str);
+ return (ISC_R_FAILURE);
+ }
+ prefix -= i;
+ prefix += DNS_RPZ_CIDR_WORD_BITS;
+ }
+
+ /*
+ * Complain about bad names but be generous and accept them.
+ */
+ if (log_level < DNS_RPZ_DEBUG_QUIET &&
+ isc_log_wouldlog(dns_lctx, log_level)) {
+ /*
+ * Convert the address back to a canonical domain name
+ * to ensure that the original name is in canonical form.
+ */
+ ip_name2 = dns_fixedname_initname(&ip_name2f);
+ result = ip2name(tgt_ip, (dns_rpz_prefix_t)prefix_num,
+ NULL, ip_name2);
+ if (result != ISC_R_SUCCESS ||
+ !dns_name_equal(&ip_name, ip_name2)) {
+ dns_name_format(ip_name2, ip2_str, sizeof(ip2_str));
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+ DNS_LOGMODULE_RBTDB, log_level,
+ "rpz IP address \"%s\""
+ " is not the canonical \"%s\"",
+ ip_str, ip2_str);
+ }
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Get trigger name and data bits for adding or deleting summary NSDNAME
+ * or QNAME data.
+ */
+static void
+name2data(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
+ dns_rpz_type_t rpz_type, const dns_name_t *src_name,
+ dns_name_t *trig_name, dns_rpz_nm_data_t *new_data)
+{
+ dns_rpz_zone_t *rpz;
+ dns_offsets_t tmp_name_offsets;
+ dns_name_t tmp_name;
+ unsigned int prefix_len, n;
+
+ REQUIRE(rpzs != NULL && rpz_num < rpzs->p.num_zones);
+ rpz = rpzs->zones[rpz_num];
+ REQUIRE(rpz != NULL);
+
+ /*
+ * Handle wildcards by putting only the parent into the
+ * summary RBT. The summary database only causes a check of the
+ * real policy zone where wildcards will be handled.
+ */
+ if (dns_name_iswildcard(src_name)) {
+ prefix_len = 1;
+ memset(&new_data->set, 0, sizeof(new_data->set));
+ make_nm_set(&new_data->wild, rpz_num, rpz_type);
+ } else {
+ prefix_len = 0;
+ make_nm_set(&new_data->set, rpz_num, rpz_type);
+ memset(&new_data->wild, 0, sizeof(new_data->wild));
+ }
+
+ dns_name_init(&tmp_name, tmp_name_offsets);
+ n = dns_name_countlabels(src_name);
+ n -= prefix_len;
+ if (rpz_type == DNS_RPZ_TYPE_QNAME)
+ n -= dns_name_countlabels(&rpz->origin);
+ else
+ n -= dns_name_countlabels(&rpz->nsdname);
+ dns_name_getlabelsequence(src_name, prefix_len, n, &tmp_name);
+ (void)dns_name_concatenate(&tmp_name, dns_rootname, trig_name, NULL);
+}
+
+#ifndef HAVE_BUILTIN_CLZ
+/**
+ * \brief Count Leading Zeros: Find the location of the left-most set
+ * bit.
+ */
+static inline unsigned int
+clz(dns_rpz_cidr_word_t w) {
+ unsigned int bit;
+
+ bit = DNS_RPZ_CIDR_WORD_BITS-1;
+
+ if ((w & 0xffff0000) != 0) {
+ w >>= 16;
+ bit -= 16;
+ }
+
+ if ((w & 0xff00) != 0) {
+ w >>= 8;
+ bit -= 8;
+ }
+
+ if ((w & 0xf0) != 0) {
+ w >>= 4;
+ bit -= 4;
+ }
+
+ if ((w & 0xc) != 0) {
+ w >>= 2;
+ bit -= 2;
+ }
+
+ if ((w & 2) != 0)
+ --bit;
+
+ return (bit);
+}
+#endif
+
+/*
+ * Find the first differing bit in two keys (IP addresses).
+ */
+static int
+diff_keys(const dns_rpz_cidr_key_t *key1, dns_rpz_prefix_t prefix1,
+ const dns_rpz_cidr_key_t *key2, dns_rpz_prefix_t prefix2)
+{
+ dns_rpz_cidr_word_t delta;
+ dns_rpz_prefix_t maxbit, bit;
+ int i;
+
+ bit = 0;
+ maxbit = ISC_MIN(prefix1, prefix2);
+
+ /*
+ * find the first differing words
+ */
+ for (i = 0; bit < maxbit; i++, bit += DNS_RPZ_CIDR_WORD_BITS) {
+ delta = key1->w[i] ^ key2->w[i];
+ if (ISC_UNLIKELY(delta != 0)) {
+#ifdef HAVE_BUILTIN_CLZ
+ bit += __builtin_clz(delta);
+#else
+ bit += clz(delta);
+#endif
+ break;
+ }
+ }
+ return (ISC_MIN(bit, maxbit));
+}
+
+/*
+ * Given a hit while searching the radix trees,
+ * clear all bits for higher numbered zones.
+ */
+static inline dns_rpz_zbits_t
+trim_zbits(dns_rpz_zbits_t zbits, dns_rpz_zbits_t found) {
+ dns_rpz_zbits_t x;
+
+ /*
+ * Isolate the first or smallest numbered hit bit.
+ * Make a mask of that bit and all smaller numbered bits.
+ */
+ x = zbits & found;
+ x &= (~x + 1);
+ x = (x << 1) - 1;
+ return (zbits &= x);
+}
+
+/*
+ * Search a radix tree for an IP address for ordinary lookup
+ * or for a CIDR block adding or deleting an entry
+ *
+ * Return ISC_R_SUCCESS, DNS_R_PARTIALMATCH, ISC_R_NOTFOUND,
+ * and *found=longest match node
+ * or with create==true, ISC_R_EXISTS or ISC_R_NOMEMORY
+ */
+static isc_result_t
+search(dns_rpz_zones_t *rpzs,
+ const dns_rpz_cidr_key_t *tgt_ip, dns_rpz_prefix_t tgt_prefix,
+ const dns_rpz_addr_zbits_t *tgt_set, bool create,
+ dns_rpz_cidr_node_t **found)
+{
+ dns_rpz_cidr_node_t *cur, *parent, *child, *new_parent, *sibling;
+ dns_rpz_addr_zbits_t set;
+ int cur_num, child_num;
+ dns_rpz_prefix_t dbit;
+ isc_result_t find_result;
+
+ set = *tgt_set;
+ find_result = ISC_R_NOTFOUND;
+ *found = NULL;
+ cur = rpzs->cidr;
+ parent = NULL;
+ cur_num = 0;
+ for (;;) {
+ if (cur == NULL) {
+ /*
+ * No child so we cannot go down.
+ * Quit with whatever we already found
+ * or add the target as a child of the current parent.
+ */
+ if (!create)
+ return (find_result);
+ child = new_node(rpzs, tgt_ip, tgt_prefix, NULL);
+ if (child == NULL)
+ return (ISC_R_NOMEMORY);
+ if (parent == NULL)
+ rpzs->cidr = child;
+ else
+ parent->child[cur_num] = child;
+ child->parent = parent;
+ child->set.client_ip |= tgt_set->client_ip;
+ child->set.ip |= tgt_set->ip;
+ child->set.nsip |= tgt_set->nsip;
+ set_sum_pair(child);
+ *found = child;
+ return (ISC_R_SUCCESS);
+ }
+
+ if ((cur->sum.client_ip & set.client_ip) == 0 &&
+ (cur->sum.ip & set.ip) == 0 &&
+ (cur->sum.nsip & set.nsip) == 0) {
+ /*
+ * This node has no relevant data
+ * and is in none of the target trees.
+ * Pretend it does not exist if we are not adding.
+ *
+ * If we are adding, continue down to eventually add
+ * a node and mark/put this node in the correct tree.
+ */
+ if (!create)
+ return (find_result);
+ }
+
+ dbit = diff_keys(tgt_ip, tgt_prefix, &cur->ip, cur->prefix);
+ /*
+ * dbit <= tgt_prefix and dbit <= cur->prefix always.
+ * We are finished searching if we matched all of the target.
+ */
+ if (dbit == tgt_prefix) {
+ if (tgt_prefix == cur->prefix) {
+ /*
+ * The node's key matches the target exactly.
+ */
+ if ((cur->set.client_ip & set.client_ip) != 0 ||
+ (cur->set.ip & set.ip) != 0 ||
+ (cur->set.nsip & set.nsip) != 0) {
+ /*
+ * It is the answer if it has data.
+ */
+ *found = cur;
+ if (create) {
+ find_result = ISC_R_EXISTS;
+ } else {
+ find_result = ISC_R_SUCCESS;
+ }
+ } else if (create) {
+ /*
+ * The node lacked relevant data,
+ * but will have it now.
+ */
+ cur->set.client_ip |= tgt_set->client_ip;
+ cur->set.ip |= tgt_set->ip;
+ cur->set.nsip |= tgt_set->nsip;
+ set_sum_pair(cur);
+ *found = cur;
+ find_result = ISC_R_SUCCESS;
+ }
+ return (find_result);
+ }
+
+ /*
+ * We know tgt_prefix < cur->prefix which means that
+ * the target is shorter than the current node.
+ * Add the target as the current node's parent.
+ */
+ if (!create)
+ return (find_result);
+
+ new_parent = new_node(rpzs, tgt_ip, tgt_prefix, cur);
+ if (new_parent == NULL)
+ return (ISC_R_NOMEMORY);
+ new_parent->parent = parent;
+ if (parent == NULL)
+ rpzs->cidr = new_parent;
+ else
+ parent->child[cur_num] = new_parent;
+ child_num = DNS_RPZ_IP_BIT(&cur->ip, tgt_prefix);
+ new_parent->child[child_num] = cur;
+ cur->parent = new_parent;
+ new_parent->set = *tgt_set;
+ set_sum_pair(new_parent);
+ *found = new_parent;
+ return (ISC_R_SUCCESS);
+ }
+
+ if (dbit == cur->prefix) {
+ if ((cur->set.client_ip & set.client_ip) != 0 ||
+ (cur->set.ip & set.ip) != 0 ||
+ (cur->set.nsip & set.nsip) != 0) {
+ /*
+ * We have a partial match between of all of the
+ * current node but only part of the target.
+ * Continue searching for other hits in the
+ * same or lower numbered trees.
+ */
+ find_result = DNS_R_PARTIALMATCH;
+ *found = cur;
+ set.client_ip = trim_zbits(set.client_ip,
+ cur->set.client_ip);
+ set.ip = trim_zbits(set.ip,
+ cur->set.ip);
+ set.nsip = trim_zbits(set.nsip,
+ cur->set.nsip);
+ }
+ parent = cur;
+ cur_num = DNS_RPZ_IP_BIT(tgt_ip, dbit);
+ cur = cur->child[cur_num];
+ continue;
+ }
+
+
+ /*
+ * dbit < tgt_prefix and dbit < cur->prefix,
+ * so we failed to match both the target and the current node.
+ * Insert a fork of a parent above the current node and
+ * add the target as a sibling of the current node
+ */
+ if (!create)
+ return (find_result);
+
+ sibling = new_node(rpzs, tgt_ip, tgt_prefix, NULL);
+ if (sibling == NULL)
+ return (ISC_R_NOMEMORY);
+ new_parent = new_node(rpzs, tgt_ip, dbit, cur);
+ if (new_parent == NULL) {
+ isc_mem_put(rpzs->mctx, sibling, sizeof(*sibling));
+ return (ISC_R_NOMEMORY);
+ }
+ new_parent->parent = parent;
+ if (parent == NULL)
+ rpzs->cidr = new_parent;
+ else
+ parent->child[cur_num] = new_parent;
+ child_num = DNS_RPZ_IP_BIT(tgt_ip, dbit);
+ new_parent->child[child_num] = sibling;
+ new_parent->child[1-child_num] = cur;
+ cur->parent = new_parent;
+ sibling->parent = new_parent;
+ sibling->set = *tgt_set;
+ set_sum_pair(sibling);
+ *found = sibling;
+ return (ISC_R_SUCCESS);
+ }
+}
+
+/*
+ * Add an IP address to the radix tree.
+ */
+static isc_result_t
+add_cidr(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
+ dns_rpz_type_t rpz_type, dns_name_t *src_name)
+{
+ dns_rpz_cidr_key_t tgt_ip;
+ dns_rpz_prefix_t tgt_prefix;
+ dns_rpz_addr_zbits_t set;
+ dns_rpz_cidr_node_t *found;
+ isc_result_t result;
+
+ result = name2ipkey(DNS_RPZ_ERROR_LEVEL, rpzs, rpz_num, rpz_type,
+ src_name, &tgt_ip, &tgt_prefix, &set);
+ /*
+ * Log complaints about bad owner names but let the zone load.
+ */
+ if (result != ISC_R_SUCCESS)
+ return (ISC_R_SUCCESS);
+
+ result = search(rpzs, &tgt_ip, tgt_prefix, &set, true, &found);
+ if (result != ISC_R_SUCCESS) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+
+ /*
+ * Do not worry if the radix tree already exists,
+ * because diff_apply() likes to add nodes before deleting.
+ */
+ if (result == ISC_R_EXISTS)
+ return (ISC_R_SUCCESS);
+
+ /*
+ * bin/tests/system/rpz/tests.sh looks for "rpz.*failed".
+ */
+ dns_name_format(src_name, namebuf, sizeof(namebuf));
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+ DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL,
+ "rpz add_cidr(%s) failed: %s",
+ namebuf, isc_result_totext(result));
+ return (result);
+ }
+
+ adj_trigger_cnt(rpzs, rpz_num, rpz_type, &tgt_ip, tgt_prefix, true);
+ return (result);
+}
+
+static isc_result_t
+add_nm(dns_rpz_zones_t *rpzs, dns_name_t *trig_name,
+ const dns_rpz_nm_data_t *new_data)
+{
+ dns_rbtnode_t *nmnode;
+ dns_rpz_nm_data_t *nm_data;
+ isc_result_t result;
+
+ nmnode = NULL;
+ result = dns_rbt_addnode(rpzs->rbt, trig_name, &nmnode);
+ switch (result) {
+ case ISC_R_SUCCESS:
+ case ISC_R_EXISTS:
+ nm_data = nmnode->data;
+ if (nm_data == NULL) {
+ nm_data = isc_mem_get(rpzs->mctx, sizeof(*nm_data));
+ if (nm_data == NULL)
+ return (ISC_R_NOMEMORY);
+ *nm_data = *new_data;
+ nmnode->data = nm_data;
+ return (ISC_R_SUCCESS);
+ }
+ break;
+ default:
+ return (result);
+ }
+
+ /*
+ * Do not count bits that are already present
+ */
+ if ((nm_data->set.qname & new_data->set.qname) != 0 ||
+ (nm_data->set.ns & new_data->set.ns) != 0 ||
+ (nm_data->wild.qname & new_data->wild.qname) != 0 ||
+ (nm_data->wild.ns & new_data->wild.ns) != 0)
+ return (ISC_R_EXISTS);
+
+ nm_data->set.qname |= new_data->set.qname;
+ nm_data->set.ns |= new_data->set.ns;
+ nm_data->wild.qname |= new_data->wild.qname;
+ nm_data->wild.ns |= new_data->wild.ns;
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+add_name(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
+ dns_rpz_type_t rpz_type, dns_name_t *src_name)
+{
+ dns_rpz_nm_data_t new_data;
+ dns_fixedname_t trig_namef;
+ dns_name_t *trig_name;
+ isc_result_t result;
+
+ /*
+ * We need a summary database of names even with 1 policy zone,
+ * because wildcard triggers are handled differently.
+ */
+
+ trig_name = dns_fixedname_initname(&trig_namef);
+ name2data(rpzs, rpz_num, rpz_type, src_name, trig_name, &new_data);
+
+ result = add_nm(rpzs, trig_name, &new_data);
+
+ /*
+ * Do not worry if the node already exists,
+ * because diff_apply() likes to add nodes before deleting.
+ */
+ if (result == ISC_R_EXISTS)
+ return (ISC_R_SUCCESS);
+ if (result == ISC_R_SUCCESS)
+ adj_trigger_cnt(rpzs, rpz_num, rpz_type, NULL, 0, true);
+ return (result);
+}
+
+/*
+ * Callback to free the data for a node in the summary RBT database.
+ */
+static void
+rpz_node_deleter(void *nm_data, void *mctx) {
+ isc_mem_put(mctx, nm_data, sizeof(dns_rpz_nm_data_t));
+}
+
+/*
+ * Get ready for a new set of policy zones for a view.
+ */
+isc_result_t
+dns_rpz_new_zones(dns_rpz_zones_t **rpzsp, isc_mem_t *mctx) {
+ dns_rpz_zones_t *new;
+ isc_result_t result;
+
+ REQUIRE(rpzsp != NULL && *rpzsp == NULL);
+
+ *rpzsp = NULL;
+
+ new = isc_mem_get(mctx, sizeof(*new));
+ if (new == NULL)
+ return (ISC_R_NOMEMORY);
+ memset(new, 0, sizeof(*new));
+
+ result = isc_rwlock_init(&new->search_lock, 0, 0);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(mctx, new, sizeof(*new));
+ return (result);
+ }
+
+ result = isc_mutex_init(&new->maint_lock);
+ if (result != ISC_R_SUCCESS) {
+ isc_rwlock_destroy(&new->search_lock);
+ isc_mem_put(mctx, new, sizeof(*new));
+ return (result);
+ }
+
+ result = isc_refcount_init(&new->refs, 1);
+ if (result != ISC_R_SUCCESS) {
+ DESTROYLOCK(&new->maint_lock);
+ isc_rwlock_destroy(&new->search_lock);
+ isc_mem_put(mctx, new, sizeof(*new));
+ return (result);
+ }
+
+ result = dns_rbt_create(mctx, rpz_node_deleter, mctx, &new->rbt);
+ if (result != ISC_R_SUCCESS) {
+ isc_refcount_decrement(&new->refs, NULL);
+ isc_refcount_destroy(&new->refs);
+ DESTROYLOCK(&new->maint_lock);
+ isc_rwlock_destroy(&new->search_lock);
+ isc_mem_put(mctx, new, sizeof(*new));
+ return (result);
+ }
+
+ isc_mem_attach(mctx, &new->mctx);
+
+ *rpzsp = new;
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Free the radix tree of a response policy database.
+ */
+static void
+cidr_free(dns_rpz_zones_t *rpzs) {
+ dns_rpz_cidr_node_t *cur, *child, *parent;
+
+ cur = rpzs->cidr;
+ while (cur != NULL) {
+ /* Depth first. */
+ child = cur->child[0];
+ if (child != NULL) {
+ cur = child;
+ continue;
+ }
+ child = cur->child[1];
+ if (child != NULL) {
+ cur = child;
+ continue;
+ }
+
+ /* Delete this leaf and go up. */
+ parent = cur->parent;
+ if (parent == NULL)
+ rpzs->cidr = NULL;
+ else
+ parent->child[parent->child[1] == cur] = NULL;
+ isc_mem_put(rpzs->mctx, cur, sizeof(*cur));
+ cur = parent;
+ }
+}
+
+/*
+ * Discard a response policy zone blob
+ * before discarding the overall rpz structure.
+ */
+static void
+rpz_detach(dns_rpz_zone_t **rpzp, dns_rpz_zones_t *rpzs) {
+ dns_rpz_zone_t *rpz;
+ unsigned int refs;
+
+ rpz = *rpzp;
+ *rpzp = NULL;
+ isc_refcount_decrement(&rpz->refs, &refs);
+ if (refs != 0)
+ return;
+ isc_refcount_destroy(&rpz->refs);
+
+ if (dns_name_dynamic(&rpz->origin))
+ dns_name_free(&rpz->origin, rpzs->mctx);
+ if (dns_name_dynamic(&rpz->client_ip))
+ dns_name_free(&rpz->client_ip, rpzs->mctx);
+ if (dns_name_dynamic(&rpz->ip))
+ dns_name_free(&rpz->ip, rpzs->mctx);
+ if (dns_name_dynamic(&rpz->nsdname))
+ dns_name_free(&rpz->nsdname, rpzs->mctx);
+ if (dns_name_dynamic(&rpz->nsip))
+ dns_name_free(&rpz->nsip, rpzs->mctx);
+ if (dns_name_dynamic(&rpz->passthru))
+ dns_name_free(&rpz->passthru, rpzs->mctx);
+ if (dns_name_dynamic(&rpz->drop))
+ dns_name_free(&rpz->drop, rpzs->mctx);
+ if (dns_name_dynamic(&rpz->tcp_only))
+ dns_name_free(&rpz->tcp_only, rpzs->mctx);
+ if (dns_name_dynamic(&rpz->cname))
+ dns_name_free(&rpz->cname, rpzs->mctx);
+
+ isc_mem_put(rpzs->mctx, rpz, sizeof(*rpz));
+}
+
+void
+dns_rpz_attach_rpzs(dns_rpz_zones_t *rpzs, dns_rpz_zones_t **rpzsp) {
+ REQUIRE(rpzsp != NULL && *rpzsp == NULL);
+ isc_refcount_increment(&rpzs->refs, NULL);
+ *rpzsp = rpzs;
+}
+
+/*
+ * Forget a view's policy zones.
+ */
+void
+dns_rpz_detach_rpzs(dns_rpz_zones_t **rpzsp) {
+ dns_rpz_zones_t *rpzs;
+ dns_rpz_zone_t *rpz;
+ dns_rpz_num_t rpz_num;
+ unsigned int refs;
+
+ REQUIRE(rpzsp != NULL);
+ rpzs = *rpzsp;
+ REQUIRE(rpzs != NULL);
+
+ *rpzsp = NULL;
+ isc_refcount_decrement(&rpzs->refs, &refs);
+ if (refs > 0)
+ return;
+
+ /*
+ * Forget the last of view's rpz machinery after the last
+ * reference.
+ */
+ for (rpz_num = 0; rpz_num < DNS_RPZ_MAX_ZONES; ++rpz_num) {
+ rpz = rpzs->zones[rpz_num];
+ rpzs->zones[rpz_num] = NULL;
+ if (rpz != NULL)
+ rpz_detach(&rpz, rpzs);
+ }
+
+ cidr_free(rpzs);
+ dns_rbt_destroy(&rpzs->rbt);
+ DESTROYLOCK(&rpzs->maint_lock);
+ isc_rwlock_destroy(&rpzs->search_lock);
+ isc_refcount_destroy(&rpzs->refs);
+ isc_mem_putanddetach(&rpzs->mctx, rpzs, sizeof(*rpzs));
+}
+
+/*
+ * Create empty summary database to load one zone.
+ * The RBTDB write tree lock must be held.
+ */
+isc_result_t
+dns_rpz_beginload(dns_rpz_zones_t **load_rpzsp,
+ dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num)
+{
+ dns_rpz_zones_t *load_rpzs;
+ dns_rpz_zone_t *rpz;
+ dns_rpz_zbits_t tgt;
+ isc_result_t result;
+
+ REQUIRE(rpz_num < rpzs->p.num_zones);
+ rpz = rpzs->zones[rpz_num];
+ REQUIRE(rpz != NULL);
+
+ /*
+ * When reloading a zone, there are usually records among the summary
+ * data for the zone. Some of those records might be deleted by the
+ * reloaded zone data. To deal with that case:
+ * reload the new zone data into a new blank summary database
+ * if the reload fails, discard the new summary database
+ * if the new zone data is acceptable, copy the records for the
+ * other zones into the new summary CIDR and RBT databases
+ * and replace the old summary databases with the new, and
+ * correct the triggers and have values for the updated
+ * zone.
+ *
+ * At the first attempt to load a zone, there is no summary data
+ * for the zone and so no records that need to be deleted.
+ * This is also the most common case of policy zone loading.
+ * Most policy zone maintenance should be by incremental changes
+ * and so by the addition and deletion of individual records.
+ * Detect that case and load records the first time into the
+ * operational summary database
+ */
+ tgt = DNS_RPZ_ZBIT(rpz_num);
+ LOCK(&rpzs->maint_lock);
+ RWLOCK(&rpzs->search_lock, isc_rwlocktype_write);
+ if ((rpzs->load_begun & tgt) == 0) {
+ /*
+ * There is no existing version of the target zone.
+ */
+ rpzs->load_begun |= tgt;
+ dns_rpz_attach_rpzs(rpzs, load_rpzsp);
+ } else {
+ /*
+ * Setup the new RPZ struct with empty summary trees.
+ */
+ result = dns_rpz_new_zones(load_rpzsp, rpzs->mctx);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ load_rpzs = *load_rpzsp;
+ /*
+ * Initialize some members so that dns_rpz_add() works.
+ */
+ load_rpzs->p.num_zones = rpzs->p.num_zones;
+ memset(&load_rpzs->triggers, 0, sizeof(load_rpzs->triggers));
+ load_rpzs->zones[rpz_num] = rpz;
+ isc_refcount_increment(&rpz->refs, NULL);
+ }
+
+ RWUNLOCK(&rpzs->search_lock, isc_rwlocktype_write);
+ UNLOCK(&rpzs->maint_lock);
+
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * This function updates "have" bits and also the qname_skip_recurse
+ * mask. It must be called when holding a write lock on rpzs->search_lock.
+ */
+static void
+fix_triggers(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num) {
+ dns_rpz_num_t n;
+ dns_rpz_triggers_t old_totals;
+ dns_rpz_zbits_t zbit;
+ char namebuf[DNS_NAME_FORMATSIZE];
+
+ /*
+ * rpzs->total_triggers is only used to log a message below.
+ */
+
+ memmove(&old_totals, &rpzs->total_triggers, sizeof(old_totals));
+ memset(&rpzs->total_triggers, 0, sizeof(rpzs->total_triggers));
+
+#define SET_TRIG(n, zbit, type) \
+ if (rpzs->triggers[n].type == 0U) { \
+ rpzs->have.type &= ~zbit; \
+ } else { \
+ rpzs->total_triggers.type += rpzs->triggers[n].type; \
+ rpzs->have.type |= zbit; \
+ }
+
+ for (n = 0; n < rpzs->p.num_zones; ++n) {
+ zbit = DNS_RPZ_ZBIT(n);
+ SET_TRIG(n, zbit, client_ipv4);
+ SET_TRIG(n, zbit, client_ipv6);
+ SET_TRIG(n, zbit, qname);
+ SET_TRIG(n, zbit, ipv4);
+ SET_TRIG(n, zbit, ipv6);
+ SET_TRIG(n, zbit, nsdname);
+ SET_TRIG(n, zbit, nsipv4);
+ SET_TRIG(n, zbit, nsipv6);
+ }
+
+#undef SET_TRIG
+
+ fix_qname_skip_recurse(rpzs);
+
+ dns_name_format(&rpzs->zones[rpz_num]->origin,
+ namebuf, sizeof(namebuf));
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+ DNS_LOGMODULE_RBTDB, DNS_RPZ_INFO_LEVEL,
+ "(re)loading policy zone '%s' changed from"
+ " %lu to %lu qname, %lu to %lu nsdname,"
+ " %lu to %lu IP, %lu to %lu NSIP,"
+ " %lu to %lu CLIENTIP entries",
+ namebuf,
+ (unsigned long) old_totals.qname,
+ (unsigned long) rpzs->total_triggers.qname,
+ (unsigned long) old_totals.nsdname,
+ (unsigned long) rpzs->total_triggers.nsdname,
+ (unsigned long) old_totals.ipv4 + old_totals.ipv6,
+ (unsigned long) (rpzs->total_triggers.ipv4 +
+ rpzs->total_triggers.ipv6),
+ (unsigned long) old_totals.nsipv4 + old_totals.nsipv6,
+ (unsigned long) (rpzs->total_triggers.nsipv4 +
+ rpzs->total_triggers.nsipv6),
+ (unsigned long) old_totals.client_ipv4 +
+ old_totals.client_ipv6,
+ (unsigned long) (rpzs->total_triggers.client_ipv4 +
+ rpzs->total_triggers.client_ipv6));
+}
+
+/*
+ * Finish loading one zone. This function is called during a commit when
+ * a RPZ zone loading is complete. The RBTDB write tree lock must be
+ * held.
+ *
+ * Here, rpzs is a pointer to the view's common rpzs
+ * structure. *load_rpzsp is a rpzs structure that is local to the
+ * RBTDB, which is used during a single zone's load.
+ *
+ * During the zone load, i.e., between dns_rpz_beginload() and
+ * dns_rpz_ready(), only the zone that is being loaded updates
+ * *load_rpzsp. These updates in the summary databases inside load_rpzsp
+ * are made only for the rpz_num (and corresponding bit) of that
+ * zone. Nothing else reads or writes *load_rpzsp. The view's common
+ * rpzs is used during this time for queries.
+ *
+ * When zone loading is complete and we arrive here, the parts of the
+ * summary databases (CIDR and nsdname+qname RBT trees) from the view's
+ * common rpzs struct have to be merged into the summary databases of
+ * *load_rpzsp, as the summary databases of the view's common rpzs
+ * struct may have changed during the time the zone was being loaded.
+ *
+ * The function below carries out the merge. During the merge, it holds
+ * the maint_lock of the view's common rpzs struct so that it is not
+ * updated while the merging is taking place.
+ *
+ * After the merging is carried out, *load_rpzsp contains the most
+ * current state of the rpzs structure, i.e., the summary trees contain
+ * data for the new zone that was just loaded, as well as all other
+ * zones.
+ *
+ * Pointers to the summary databases of *load_rpzsp (CIDR and
+ * nsdname+qname RBT trees) are then swapped into the view's common rpz
+ * struct, so that the query path can continue using it. During the
+ * swap, the search_lock of the view's common rpz struct is acquired so
+ * that queries are paused while this swap occurs.
+ *
+ * The trigger counts for the new zone are also copied into the view's
+ * common rpz struct, and some other summary counts and masks are
+ * updated.
+ */
+isc_result_t
+dns_rpz_ready(dns_rpz_zones_t *rpzs,
+ dns_rpz_zones_t **load_rpzsp, dns_rpz_num_t rpz_num)
+{
+ dns_rpz_zones_t *load_rpzs;
+ const dns_rpz_cidr_node_t *cnode, *next_cnode, *parent_cnode;
+ dns_rpz_cidr_node_t *found;
+ dns_rpz_zbits_t new_bit;
+ dns_rpz_addr_zbits_t new_ip;
+ dns_rbt_t *rbt;
+ dns_rbtnodechain_t chain;
+ dns_rbtnode_t *nmnode;
+ dns_rpz_nm_data_t *nm_data, new_data;
+ dns_fixedname_t labelf, originf, namef;
+ dns_name_t *label, *origin, *name;
+ isc_result_t result;
+
+ INSIST(rpzs != NULL);
+ LOCK(&rpzs->maint_lock);
+ load_rpzs = *load_rpzsp;
+ INSIST(load_rpzs != NULL);
+
+ if (load_rpzs == rpzs) {
+ /*
+ * This is a successful initial zone loading, perhaps
+ * for a new instance of a view.
+ */
+ RWLOCK(&rpzs->search_lock, isc_rwlocktype_write);
+ fix_triggers(rpzs, rpz_num);
+ RWUNLOCK(&rpzs->search_lock, isc_rwlocktype_write);
+ UNLOCK(&rpzs->maint_lock);
+ dns_rpz_detach_rpzs(load_rpzsp);
+ return (ISC_R_SUCCESS);
+ }
+
+ LOCK(&load_rpzs->maint_lock);
+ RWLOCK(&load_rpzs->search_lock, isc_rwlocktype_write);
+
+ /*
+ * Unless there is only one policy zone, copy the other policy zones
+ * from the old policy structure to the new summary databases.
+ */
+ if (rpzs->p.num_zones > 1) {
+ new_bit = ~DNS_RPZ_ZBIT(rpz_num);
+
+ /*
+ * Copy to the radix tree.
+ */
+ for (cnode = rpzs->cidr; cnode != NULL; cnode = next_cnode) {
+ new_ip.ip = cnode->set.ip & new_bit;
+ new_ip.client_ip = cnode->set.client_ip & new_bit;
+ new_ip.nsip = cnode->set.nsip & new_bit;
+ if (new_ip.client_ip != 0 ||
+ new_ip.ip != 0 ||
+ new_ip.nsip != 0) {
+ result = search(load_rpzs,
+ &cnode->ip, cnode->prefix,
+ &new_ip, true, &found);
+ if (result == ISC_R_NOMEMORY)
+ goto unlock_and_detach;
+ INSIST(result == ISC_R_SUCCESS);
+ }
+ /*
+ * Do down and to the left as far as possible.
+ */
+ next_cnode = cnode->child[0];
+ if (next_cnode != NULL)
+ continue;
+ /*
+ * Go up until we find a branch to the right where
+ * we previously took the branch to the left.
+ */
+ for (;;) {
+ parent_cnode = cnode->parent;
+ if (parent_cnode == NULL)
+ break;
+ if (parent_cnode->child[0] == cnode) {
+ next_cnode = parent_cnode->child[1];
+ if (next_cnode != NULL)
+ break;
+ }
+ cnode = parent_cnode;
+ }
+ }
+
+ /*
+ * Copy to the summary RBT.
+ */
+ dns_fixedname_init(&namef);
+ name = dns_fixedname_name(&namef);
+ dns_fixedname_init(&labelf);
+ label = dns_fixedname_name(&labelf);
+ dns_fixedname_init(&originf);
+ origin = dns_fixedname_name(&originf);
+ dns_rbtnodechain_init(&chain, NULL);
+ result = dns_rbtnodechain_first(&chain, rpzs->rbt, NULL, NULL);
+ while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) {
+ result = dns_rbtnodechain_current(&chain, label, origin,
+ &nmnode);
+ INSIST(result == ISC_R_SUCCESS);
+ nm_data = nmnode->data;
+ if (nm_data != NULL) {
+ new_data.set.qname = (nm_data->set.qname &
+ new_bit);
+ new_data.set.ns = nm_data->set.ns & new_bit;
+ new_data.wild.qname = (nm_data->wild.qname &
+ new_bit);
+ new_data.wild.ns = nm_data->wild.ns & new_bit;
+ if (new_data.set.qname != 0 ||
+ new_data.set.ns != 0 ||
+ new_data.wild.qname != 0 ||
+ new_data.wild.ns != 0) {
+ result = dns_name_concatenate(label,
+ origin, name, NULL);
+ INSIST(result == ISC_R_SUCCESS);
+ result = add_nm(load_rpzs, name,
+ &new_data);
+ if (result != ISC_R_SUCCESS)
+ goto unlock_and_detach;
+ }
+ }
+ result = dns_rbtnodechain_next(&chain, NULL, NULL);
+ }
+ if (result != ISC_R_NOMORE && result != ISC_R_NOTFOUND) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+ DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL,
+ "dns_rpz_ready(): unexpected %s",
+ isc_result_totext(result));
+ goto unlock_and_detach;
+ }
+ }
+
+ /*
+ * Exchange the summary databases.
+ */
+ RWLOCK(&rpzs->search_lock, isc_rwlocktype_write);
+
+ rpzs->triggers[rpz_num] = load_rpzs->triggers[rpz_num];
+ fix_triggers(rpzs, rpz_num);
+
+ found = rpzs->cidr;
+ rpzs->cidr = load_rpzs->cidr;
+ load_rpzs->cidr = found;
+
+ rbt = rpzs->rbt;
+ rpzs->rbt = load_rpzs->rbt;
+ load_rpzs->rbt = rbt;
+
+ RWUNLOCK(&rpzs->search_lock, isc_rwlocktype_write);
+
+ result = ISC_R_SUCCESS;
+
+ unlock_and_detach:
+ UNLOCK(&rpzs->maint_lock);
+ RWUNLOCK(&load_rpzs->search_lock, isc_rwlocktype_write);
+ UNLOCK(&load_rpzs->maint_lock);
+ dns_rpz_detach_rpzs(load_rpzsp);
+ return (result);
+}
+
+/*
+ * Add an IP address to the radix tree or a name to the summary database.
+ */
+isc_result_t
+dns_rpz_add(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num, dns_name_t *src_name)
+{
+ dns_rpz_zone_t *rpz;
+ dns_rpz_type_t rpz_type;
+ isc_result_t result = ISC_R_FAILURE;
+
+ REQUIRE(rpzs != NULL && rpz_num < rpzs->p.num_zones);
+ rpz = rpzs->zones[rpz_num];
+ REQUIRE(rpz != NULL);
+
+ rpz_type = type_from_name(rpz, src_name);
+
+ LOCK(&rpzs->maint_lock);
+ RWLOCK(&rpzs->search_lock, isc_rwlocktype_write);
+
+ switch (rpz_type) {
+ case DNS_RPZ_TYPE_QNAME:
+ case DNS_RPZ_TYPE_NSDNAME:
+ result = add_name(rpzs, rpz_num, rpz_type, src_name);
+ break;
+ case DNS_RPZ_TYPE_CLIENT_IP:
+ case DNS_RPZ_TYPE_IP:
+ case DNS_RPZ_TYPE_NSIP:
+ result = add_cidr(rpzs, rpz_num, rpz_type, src_name);
+ break;
+ case DNS_RPZ_TYPE_BAD:
+ break;
+ }
+
+ RWUNLOCK(&rpzs->search_lock, isc_rwlocktype_write);
+ UNLOCK(&rpzs->maint_lock);
+ return (result);
+}
+
+/*
+ * Remove an IP address from the radix tree.
+ */
+static void
+del_cidr(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
+ dns_rpz_type_t rpz_type, dns_name_t *src_name)
+{
+ isc_result_t result;
+ dns_rpz_cidr_key_t tgt_ip;
+ dns_rpz_prefix_t tgt_prefix;
+ dns_rpz_addr_zbits_t tgt_set;
+ dns_rpz_cidr_node_t *tgt, *parent, *child;
+
+ /*
+ * Do not worry about invalid rpz IP address names. If we
+ * are here, then something relevant was added and so was
+ * valid. Invalid names here are usually internal RBTDB nodes.
+ */
+ result = name2ipkey(DNS_RPZ_DEBUG_QUIET, rpzs, rpz_num, rpz_type,
+ src_name, &tgt_ip, &tgt_prefix, &tgt_set);
+ if (result != ISC_R_SUCCESS)
+ return;
+
+ result = search(rpzs, &tgt_ip, tgt_prefix, &tgt_set, false, &tgt);
+ if (result != ISC_R_SUCCESS) {
+ INSIST(result == ISC_R_NOTFOUND ||
+ result == DNS_R_PARTIALMATCH);
+ /*
+ * Do not worry about missing summary RBT nodes that probably
+ * correspond to RBTDB nodes that were implicit RBT nodes
+ * that were later added for (often empty) wildcards
+ * and then to the RBTDB deferred cleanup list.
+ */
+ return;
+ }
+
+ /*
+ * Mark the node and its parents to reflect the deleted IP address.
+ * Do not count bits that are already clear for internal RBTDB nodes.
+ */
+ tgt_set.client_ip &= tgt->set.client_ip;
+ tgt_set.ip &= tgt->set.ip;
+ tgt_set.nsip &= tgt->set.nsip;
+ tgt->set.client_ip &= ~tgt_set.client_ip;
+ tgt->set.ip &= ~tgt_set.ip;
+ tgt->set.nsip &= ~tgt_set.nsip;
+ set_sum_pair(tgt);
+
+ adj_trigger_cnt(rpzs, rpz_num, rpz_type, &tgt_ip, tgt_prefix, false);
+
+ /*
+ * We might need to delete 2 nodes.
+ */
+ do {
+ /*
+ * The node is now useless if it has no data of its own
+ * and 0 or 1 children. We are finished if it is not useless.
+ */
+ if ((child = tgt->child[0]) != NULL) {
+ if (tgt->child[1] != NULL)
+ break;
+ } else {
+ child = tgt->child[1];
+ }
+ if (tgt->set.client_ip != 0 ||
+ tgt->set.ip != 0 ||
+ tgt->set.nsip != 0)
+ break;
+
+ /*
+ * Replace the pointer to this node in the parent with
+ * the remaining child or NULL.
+ */
+ parent = tgt->parent;
+ if (parent == NULL) {
+ rpzs->cidr = child;
+ } else {
+ parent->child[parent->child[1] == tgt] = child;
+ }
+ /*
+ * If the child exists fix up its parent pointer.
+ */
+ if (child != NULL)
+ child->parent = parent;
+ isc_mem_put(rpzs->mctx, tgt, sizeof(*tgt));
+
+ tgt = parent;
+ } while (tgt != NULL);
+}
+
+static void
+del_name(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
+ dns_rpz_type_t rpz_type, dns_name_t *src_name)
+{
+ char namebuf[DNS_NAME_FORMATSIZE];
+ dns_fixedname_t trig_namef;
+ dns_name_t *trig_name;
+ dns_rbtnode_t *nmnode;
+ dns_rpz_nm_data_t *nm_data, del_data;
+ isc_result_t result;
+ bool exists;
+
+ /*
+ * We need a summary database of names even with 1 policy zone,
+ * because wildcard triggers are handled differently.
+ */
+
+ trig_name = dns_fixedname_initname(&trig_namef);
+ name2data(rpzs, rpz_num, rpz_type, src_name, trig_name, &del_data);
+
+ nmnode = NULL;
+ result = dns_rbt_findnode(rpzs->rbt, trig_name, NULL, &nmnode, NULL, 0,
+ NULL, NULL);
+ if (result != ISC_R_SUCCESS) {
+ /*
+ * Do not worry about missing summary RBT nodes that probably
+ * correspond to RBTDB nodes that were implicit RBT nodes
+ * that were later added for (often empty) wildcards
+ * and then to the RBTDB deferred cleanup list.
+ */
+ if (result == ISC_R_NOTFOUND ||
+ result == DNS_R_PARTIALMATCH)
+ return;
+ dns_name_format(src_name, namebuf, sizeof(namebuf));
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+ DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL,
+ "rpz del_name(%s) node search failed: %s",
+ namebuf, isc_result_totext(result));
+ return;
+ }
+
+ nm_data = nmnode->data;
+ INSIST(nm_data != NULL);
+
+ /*
+ * Do not count bits that next existed for RBT nodes that would we
+ * would not have found in a summary for a single RBTDB tree.
+ */
+ del_data.set.qname &= nm_data->set.qname;
+ del_data.set.ns &= nm_data->set.ns;
+ del_data.wild.qname &= nm_data->wild.qname;
+ del_data.wild.ns &= nm_data->wild.ns;
+
+ exists = (del_data.set.qname != 0 ||
+ del_data.set.ns != 0 ||
+ del_data.wild.qname != 0 ||
+ del_data.wild.ns != 0);
+
+ nm_data->set.qname &= ~del_data.set.qname;
+ nm_data->set.ns &= ~del_data.set.ns;
+ nm_data->wild.qname &= ~del_data.wild.qname;
+ nm_data->wild.ns &= ~del_data.wild.ns;
+
+ if (nm_data->set.qname == 0 && nm_data->set.ns == 0 &&
+ nm_data->wild.qname == 0 && nm_data->wild.ns == 0) {
+ result = dns_rbt_deletenode(rpzs->rbt, nmnode, false);
+ if (result != ISC_R_SUCCESS) {
+ /*
+ * bin/tests/system/rpz/tests.sh looks for "rpz.*failed".
+ */
+ dns_name_format(src_name, namebuf, sizeof(namebuf));
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+ DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL,
+ "rpz del_name(%s) node delete failed: %s",
+ namebuf, isc_result_totext(result));
+ }
+ }
+
+ if (exists)
+ adj_trigger_cnt(rpzs, rpz_num, rpz_type, NULL, 0, false);
+}
+
+/*
+ * Remove an IP address from the radix tree or a name from the summary database.
+ */
+void
+dns_rpz_delete(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
+ dns_name_t *src_name) {
+ dns_rpz_zone_t *rpz;
+ dns_rpz_type_t rpz_type;
+
+ REQUIRE(rpzs != NULL && rpz_num < rpzs->p.num_zones);
+ rpz = rpzs->zones[rpz_num];
+ REQUIRE(rpz != NULL);
+
+ rpz_type = type_from_name(rpz, src_name);
+
+ LOCK(&rpzs->maint_lock);
+ RWLOCK(&rpzs->search_lock, isc_rwlocktype_write);
+
+ switch (rpz_type) {
+ case DNS_RPZ_TYPE_QNAME:
+ case DNS_RPZ_TYPE_NSDNAME:
+ del_name(rpzs, rpz_num, rpz_type, src_name);
+ break;
+ case DNS_RPZ_TYPE_CLIENT_IP:
+ case DNS_RPZ_TYPE_IP:
+ case DNS_RPZ_TYPE_NSIP:
+ del_cidr(rpzs, rpz_num, rpz_type, src_name);
+ break;
+ case DNS_RPZ_TYPE_BAD:
+ break;
+ }
+
+ RWUNLOCK(&rpzs->search_lock, isc_rwlocktype_write);
+ UNLOCK(&rpzs->maint_lock);
+}
+
+/*
+ * Search the summary radix tree to get a relative owner name in a
+ * policy zone relevant to a triggering IP address.
+ * rpz_type and zbits limit the search for IP address netaddr
+ * return the policy zone's number or DNS_RPZ_INVALID_NUM
+ * ip_name is the relative owner name found and
+ * *prefixp is its prefix length.
+ */
+dns_rpz_num_t
+dns_rpz_find_ip(dns_rpz_zones_t *rpzs, dns_rpz_type_t rpz_type,
+ dns_rpz_zbits_t zbits, const isc_netaddr_t *netaddr,
+ dns_name_t *ip_name, dns_rpz_prefix_t *prefixp)
+{
+ dns_rpz_cidr_key_t tgt_ip;
+ dns_rpz_addr_zbits_t tgt_set;
+ dns_rpz_cidr_node_t *found;
+ isc_result_t result;
+ dns_rpz_num_t rpz_num;
+ dns_rpz_have_t have;
+ int i;
+
+ LOCK(&rpzs->maint_lock);
+ have = rpzs->have;
+ UNLOCK(&rpzs->maint_lock);
+
+ /*
+ * Convert IP address to CIDR tree key.
+ */
+ if (netaddr->family == AF_INET) {
+ tgt_ip.w[0] = 0;
+ tgt_ip.w[1] = 0;
+ tgt_ip.w[2] = ADDR_V4MAPPED;
+ tgt_ip.w[3] = ntohl(netaddr->type.in.s_addr);
+ switch (rpz_type) {
+ case DNS_RPZ_TYPE_CLIENT_IP:
+ zbits &= have.client_ipv4;
+ break;
+ case DNS_RPZ_TYPE_IP:
+ zbits &= have.ipv4;
+ break;
+ case DNS_RPZ_TYPE_NSIP:
+ zbits &= have.nsipv4;
+ break;
+ default:
+ INSIST(0);
+ break;
+ }
+ } else if (netaddr->family == AF_INET6) {
+ dns_rpz_cidr_key_t src_ip6;
+
+ /*
+ * Given the int aligned struct in_addr member of netaddr->type
+ * one could cast netaddr->type.in6 to dns_rpz_cidr_key_t *,
+ * but some people object.
+ */
+ memmove(src_ip6.w, &netaddr->type.in6, sizeof(src_ip6.w));
+ for (i = 0; i < 4; i++) {
+ tgt_ip.w[i] = ntohl(src_ip6.w[i]);
+ }
+ switch (rpz_type) {
+ case DNS_RPZ_TYPE_CLIENT_IP:
+ zbits &= have.client_ipv6;
+ break;
+ case DNS_RPZ_TYPE_IP:
+ zbits &= have.ipv6;
+ break;
+ case DNS_RPZ_TYPE_NSIP:
+ zbits &= have.nsipv6;
+ break;
+ default:
+ INSIST(0);
+ break;
+ }
+ } else {
+ return (DNS_RPZ_INVALID_NUM);
+ }
+
+ if (zbits == 0)
+ return (DNS_RPZ_INVALID_NUM);
+ make_addr_set(&tgt_set, zbits, rpz_type);
+
+ RWLOCK(&rpzs->search_lock, isc_rwlocktype_read);
+ result = search(rpzs, &tgt_ip, 128, &tgt_set, false, &found);
+ if (result == ISC_R_NOTFOUND) {
+ /*
+ * There are no eligible zones for this IP address.
+ */
+ RWUNLOCK(&rpzs->search_lock, isc_rwlocktype_read);
+ return (DNS_RPZ_INVALID_NUM);
+ }
+
+ /*
+ * Construct the trigger name for the longest matching trigger
+ * in the first eligible zone with a match.
+ */
+ *prefixp = found->prefix;
+ switch (rpz_type) {
+ case DNS_RPZ_TYPE_CLIENT_IP:
+ rpz_num = zbit_to_num(found->set.client_ip & tgt_set.client_ip);
+ break;
+ case DNS_RPZ_TYPE_IP:
+ rpz_num = zbit_to_num(found->set.ip & tgt_set.ip);
+ break;
+ case DNS_RPZ_TYPE_NSIP:
+ rpz_num = zbit_to_num(found->set.nsip & tgt_set.nsip);
+ break;
+ default:
+ INSIST(0);
+ break;
+ }
+ result = ip2name(&found->ip, found->prefix, dns_rootname, ip_name);
+ RWUNLOCK(&rpzs->search_lock, isc_rwlocktype_read);
+ if (result != ISC_R_SUCCESS) {
+ /*
+ * bin/tests/system/rpz/tests.sh looks for "rpz.*failed".
+ */
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+ DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL,
+ "rpz ip2name() failed: %s",
+ isc_result_totext(result));
+ return (DNS_RPZ_INVALID_NUM);
+ }
+ return (rpz_num);
+}
+
+/*
+ * Search the summary radix tree for policy zones with triggers matching
+ * a name.
+ */
+dns_rpz_zbits_t
+dns_rpz_find_name(dns_rpz_zones_t *rpzs, dns_rpz_type_t rpz_type,
+ dns_rpz_zbits_t zbits, dns_name_t *trig_name)
+{
+ char namebuf[DNS_NAME_FORMATSIZE];
+ dns_rbtnode_t *nmnode;
+ const dns_rpz_nm_data_t *nm_data;
+ dns_rpz_zbits_t found_zbits;
+ isc_result_t result;
+
+ if (zbits == 0)
+ return (0);
+
+ found_zbits = 0;
+
+ RWLOCK(&rpzs->search_lock, isc_rwlocktype_read);
+
+ nmnode = NULL;
+ result = dns_rbt_findnode(rpzs->rbt, trig_name, NULL, &nmnode, NULL,
+ DNS_RBTFIND_EMPTYDATA, NULL, NULL);
+ switch (result) {
+ case ISC_R_SUCCESS:
+ nm_data = nmnode->data;
+ if (nm_data != NULL) {
+ if (rpz_type == DNS_RPZ_TYPE_QNAME)
+ found_zbits = nm_data->set.qname;
+ else
+ found_zbits = nm_data->set.ns;
+ }
+ nmnode = nmnode->parent;
+ /* fall thru */
+ case DNS_R_PARTIALMATCH:
+ while (nmnode != NULL) {
+ nm_data = nmnode->data;
+ if (nm_data != NULL) {
+ if (rpz_type == DNS_RPZ_TYPE_QNAME)
+ found_zbits |= nm_data->wild.qname;
+ else
+ found_zbits |= nm_data->wild.ns;
+ }
+ nmnode = nmnode->parent;
+ }
+ break;
+
+ case ISC_R_NOTFOUND:
+ break;
+
+ default:
+ /*
+ * bin/tests/system/rpz/tests.sh looks for "rpz.*failed".
+ */
+ dns_name_format(trig_name, namebuf, sizeof(namebuf));
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+ DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL,
+ "dns_rpz_find_name(%s) failed: %s",
+ namebuf, isc_result_totext(result));
+ break;
+ }
+
+ RWUNLOCK(&rpzs->search_lock, isc_rwlocktype_read);
+ return (zbits & found_zbits);
+}
+
+/*
+ * Translate CNAME rdata to a QNAME response policy action.
+ */
+dns_rpz_policy_t
+dns_rpz_decode_cname(dns_rpz_zone_t *rpz, dns_rdataset_t *rdataset,
+ dns_name_t *selfname)
+{
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_cname_t cname;
+ isc_result_t result;
+
+ result = dns_rdataset_first(rdataset);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_rdataset_current(rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &cname, NULL);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_rdata_reset(&rdata);
+
+ /*
+ * CNAME . means NXDOMAIN
+ */
+ if (dns_name_equal(&cname.cname, dns_rootname))
+ return (DNS_RPZ_POLICY_NXDOMAIN);
+
+ if (dns_name_iswildcard(&cname.cname)) {
+ /*
+ * CNAME *. means NODATA
+ */
+ if (dns_name_countlabels(&cname.cname) == 2)
+ return (DNS_RPZ_POLICY_NODATA);
+
+ /*
+ * A qname of www.evil.com and a policy of
+ * *.evil.com CNAME *.garden.net
+ * gives a result of
+ * evil.com CNAME evil.com.garden.net
+ */
+ if (dns_name_countlabels(&cname.cname) > 2)
+ return (DNS_RPZ_POLICY_WILDCNAME);
+ }
+
+ /*
+ * CNAME rpz-tcp-only. means "send truncated UDP responses."
+ */
+ if (dns_name_equal(&cname.cname, &rpz->tcp_only))
+ return (DNS_RPZ_POLICY_TCP_ONLY);
+
+ /*
+ * CNAME rpz-drop. means "do not respond."
+ */
+ if (dns_name_equal(&cname.cname, &rpz->drop))
+ return (DNS_RPZ_POLICY_DROP);
+
+ /*
+ * CNAME rpz-passthru. means "do not rewrite."
+ */
+ if (dns_name_equal(&cname.cname, &rpz->passthru))
+ return (DNS_RPZ_POLICY_PASSTHRU);
+
+ /*
+ * 128.1.0.127.rpz-ip CNAME 128.1.0.0.127. is obsolete PASSTHRU
+ */
+ if (selfname != NULL && dns_name_equal(&cname.cname, selfname))
+ return (DNS_RPZ_POLICY_PASSTHRU);
+
+ /*
+ * Any other rdata gives a response consisting of the rdata.
+ */
+ return (DNS_RPZ_POLICY_RECORD);
+}
diff --git a/lib/dns/rriterator.c b/lib/dns/rriterator.c
new file mode 100644
index 0000000..3a6b9a1
--- /dev/null
+++ b/lib/dns/rriterator.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+/***
+ *** Imports
+ ***/
+
+#include <config.h>
+
+#include <inttypes.h>
+
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dns/db.h>
+#include <dns/dbiterator.h>
+#include <dns/rdata.h>
+#include <dns/rdataset.h>
+#include <dns/rdatasetiter.h>
+#include <dns/result.h>
+#include <dns/rriterator.h>
+
+/***
+ *** RRiterator methods
+ ***/
+
+isc_result_t
+dns_rriterator_init(dns_rriterator_t *it, dns_db_t *db, dns_dbversion_t *ver,
+ isc_stdtime_t now)
+{
+ isc_result_t result;
+ it->magic = RRITERATOR_MAGIC;
+ it->db = db;
+ it->dbit = NULL;
+ it->ver = ver;
+ it->now = now;
+ it->node = NULL;
+ result = dns_db_createiterator(it->db, 0, &it->dbit);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ it->rdatasetit = NULL;
+ dns_rdata_init(&it->rdata);
+ dns_rdataset_init(&it->rdataset);
+ dns_fixedname_init(&it->fixedname);
+ INSIST(! dns_rdataset_isassociated(&it->rdataset));
+ it->result = ISC_R_SUCCESS;
+ return (it->result);
+}
+
+isc_result_t
+dns_rriterator_first(dns_rriterator_t *it) {
+ REQUIRE(VALID_RRITERATOR(it));
+ /* Reset state */
+ if (dns_rdataset_isassociated(&it->rdataset))
+ dns_rdataset_disassociate(&it->rdataset);
+ if (it->rdatasetit != NULL)
+ dns_rdatasetiter_destroy(&it->rdatasetit);
+ if (it->node != NULL)
+ dns_db_detachnode(it->db, &it->node);
+ it->result = dns_dbiterator_first(it->dbit);
+
+ /*
+ * The top node may be empty when out of zone glue exists.
+ * Walk the tree to find the first node with data.
+ */
+ while (it->result == ISC_R_SUCCESS) {
+ it->result = dns_dbiterator_current(it->dbit, &it->node,
+ dns_fixedname_name(&it->fixedname));
+ if (it->result != ISC_R_SUCCESS)
+ return (it->result);
+
+ it->result = dns_db_allrdatasets(it->db, it->node, it->ver,
+ it->now, &it->rdatasetit);
+ if (it->result != ISC_R_SUCCESS)
+ return (it->result);
+
+ it->result = dns_rdatasetiter_first(it->rdatasetit);
+ if (it->result != ISC_R_SUCCESS) {
+ /*
+ * This node is empty. Try next node.
+ */
+ dns_rdatasetiter_destroy(&it->rdatasetit);
+ dns_db_detachnode(it->db, &it->node);
+ it->result = dns_dbiterator_next(it->dbit);
+ continue;
+ }
+ dns_rdatasetiter_current(it->rdatasetit, &it->rdataset);
+ dns_rdataset_getownercase(&it->rdataset,
+ dns_fixedname_name(&it->fixedname));
+ it->rdataset.attributes |= DNS_RDATASETATTR_LOADORDER;
+ it->result = dns_rdataset_first(&it->rdataset);
+ return (it->result);
+ }
+ return (it->result);
+}
+
+isc_result_t
+dns_rriterator_nextrrset(dns_rriterator_t *it) {
+ REQUIRE(VALID_RRITERATOR(it));
+ if (dns_rdataset_isassociated(&it->rdataset))
+ dns_rdataset_disassociate(&it->rdataset);
+ it->result = dns_rdatasetiter_next(it->rdatasetit);
+ /*
+ * The while loop body is executed more than once
+ * only when an empty dbnode needs to be skipped.
+ */
+ while (it->result == ISC_R_NOMORE) {
+ dns_rdatasetiter_destroy(&it->rdatasetit);
+ dns_db_detachnode(it->db, &it->node);
+ it->result = dns_dbiterator_next(it->dbit);
+ if (it->result == ISC_R_NOMORE) {
+ /* We are at the end of the entire database. */
+ return (it->result);
+ }
+ if (it->result != ISC_R_SUCCESS)
+ return (it->result);
+ it->result = dns_dbiterator_current(it->dbit, &it->node,
+ dns_fixedname_name(&it->fixedname));
+ if (it->result != ISC_R_SUCCESS)
+ return (it->result);
+ it->result = dns_db_allrdatasets(it->db, it->node, it->ver,
+ it->now, &it->rdatasetit);
+ if (it->result != ISC_R_SUCCESS)
+ return (it->result);
+ it->result = dns_rdatasetiter_first(it->rdatasetit);
+ }
+ if (it->result != ISC_R_SUCCESS)
+ return (it->result);
+ dns_rdatasetiter_current(it->rdatasetit, &it->rdataset);
+ dns_rdataset_getownercase(&it->rdataset,
+ dns_fixedname_name(&it->fixedname));
+ it->rdataset.attributes |= DNS_RDATASETATTR_LOADORDER;
+ it->result = dns_rdataset_first(&it->rdataset);
+ return (it->result);
+}
+
+isc_result_t
+dns_rriterator_next(dns_rriterator_t *it) {
+ REQUIRE(VALID_RRITERATOR(it));
+ if (it->result != ISC_R_SUCCESS)
+ return (it->result);
+
+ INSIST(it->dbit != NULL);
+ INSIST(it->node != NULL);
+ INSIST(it->rdatasetit != NULL);
+
+ it->result = dns_rdataset_next(&it->rdataset);
+ if (it->result == ISC_R_NOMORE)
+ return (dns_rriterator_nextrrset(it));
+ return (it->result);
+}
+
+void
+dns_rriterator_pause(dns_rriterator_t *it) {
+ REQUIRE(VALID_RRITERATOR(it));
+ RUNTIME_CHECK(dns_dbiterator_pause(it->dbit) == ISC_R_SUCCESS);
+}
+
+void
+dns_rriterator_destroy(dns_rriterator_t *it) {
+ REQUIRE(VALID_RRITERATOR(it));
+ if (dns_rdataset_isassociated(&it->rdataset))
+ dns_rdataset_disassociate(&it->rdataset);
+ if (it->rdatasetit != NULL)
+ dns_rdatasetiter_destroy(&it->rdatasetit);
+ if (it->node != NULL)
+ dns_db_detachnode(it->db, &it->node);
+ dns_dbiterator_destroy(&it->dbit);
+}
+
+void
+dns_rriterator_current(dns_rriterator_t *it, dns_name_t **name,
+ uint32_t *ttl, dns_rdataset_t **rdataset,
+ dns_rdata_t **rdata)
+{
+ REQUIRE(name != NULL && *name == NULL);
+ REQUIRE(VALID_RRITERATOR(it));
+ REQUIRE(it->result == ISC_R_SUCCESS);
+ REQUIRE(rdataset == NULL || *rdataset == NULL);
+ REQUIRE(rdata == NULL || *rdata == NULL);
+
+ *name = dns_fixedname_name(&it->fixedname);
+ *ttl = it->rdataset.ttl;
+
+ dns_rdata_reset(&it->rdata);
+ dns_rdataset_current(&it->rdataset, &it->rdata);
+
+ if (rdataset != NULL)
+ *rdataset = &it->rdataset;
+
+ if (rdata != NULL)
+ *rdata = &it->rdata;
+}
diff --git a/lib/dns/rrl.c b/lib/dns/rrl.c
new file mode 100644
index 0000000..cb7e7b6
--- /dev/null
+++ b/lib/dns/rrl.c
@@ -0,0 +1,1320 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+/*
+ * Rate limit DNS responses.
+ */
+
+/* #define ISC_LIST_CHECKINIT */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/mem.h>
+#include <isc/net.h>
+#include <isc/netaddr.h>
+#include <isc/print.h>
+#include <isc/util.h>
+
+#include <dns/result.h>
+#include <dns/rcode.h>
+#include <dns/rdatatype.h>
+#include <dns/rdataclass.h>
+#include <dns/log.h>
+#include <dns/rrl.h>
+#include <dns/view.h>
+
+static void
+log_end(dns_rrl_t *rrl, dns_rrl_entry_t *e, bool early,
+ char *log_buf, unsigned int log_buf_len);
+
+/*
+ * Get a modulus for a hash function that is tolerably likely to be
+ * relatively prime to most inputs. Of course, we get a prime for for initial
+ * values not larger than the square of the last prime. We often get a prime
+ * after that.
+ * This works well in practice for hash tables up to at least 100
+ * times the square of the last prime and better than a multiplicative hash.
+ */
+static int
+hash_divisor(unsigned int initial) {
+ static uint16_t primes[] = {
+ 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41,
+ 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97,
+#if 0
+ 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157,
+ 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227,
+ 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283,
+ 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367,
+ 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439,
+ 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509,
+ 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599,
+ 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661,
+ 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751,
+ 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829,
+ 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919,
+ 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997,1009,
+#endif
+ };
+ int divisions, tries;
+ unsigned int result;
+ uint16_t *pp, p;
+
+ result = initial;
+
+ if (primes[sizeof(primes)/sizeof(primes[0])-1] >= result) {
+ pp = primes;
+ while (*pp < result)
+ ++pp;
+ return (*pp);
+ }
+
+ if ((result & 1) == 0)
+ ++result;
+
+ divisions = 0;
+ tries = 1;
+ pp = primes;
+ do {
+ p = *pp++;
+ ++divisions;
+ if ((result % p) == 0) {
+ ++tries;
+ result += 2;
+ pp = primes;
+ }
+ } while (pp < &primes[sizeof(primes) / sizeof(primes[0])]);
+
+ if (isc_log_wouldlog(dns_lctx, DNS_RRL_LOG_DEBUG3))
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
+ DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_DEBUG3,
+ "%d hash_divisor() divisions in %d tries"
+ " to get %d from %d",
+ divisions, tries, result, initial);
+
+ return (result);
+}
+
+/*
+ * Convert a timestamp to a number of seconds in the past.
+ */
+static inline int
+delta_rrl_time(isc_stdtime_t ts, isc_stdtime_t now) {
+ int delta;
+
+ delta = now - ts;
+ if (delta >= 0)
+ return (delta);
+
+ /*
+ * The timestamp is in the future. That future might result from
+ * re-ordered requests, because we use timestamps on requests
+ * instead of consulting a clock. Timestamps in the distant future are
+ * assumed to result from clock changes. When the clock changes to
+ * the past, make existing timestamps appear to be in the past.
+ */
+ if (delta < -DNS_RRL_MAX_TIME_TRAVEL)
+ return (DNS_RRL_FOREVER);
+ return (0);
+}
+
+static inline int
+get_age(const dns_rrl_t *rrl, const dns_rrl_entry_t *e, isc_stdtime_t now) {
+ if (!e->ts_valid)
+ return (DNS_RRL_FOREVER);
+ return (delta_rrl_time(e->ts + rrl->ts_bases[e->ts_gen], now));
+}
+
+static inline void
+set_age(dns_rrl_t *rrl, dns_rrl_entry_t *e, isc_stdtime_t now) {
+ dns_rrl_entry_t *e_old;
+ unsigned int ts_gen;
+ int i, ts;
+
+ ts_gen = rrl->ts_gen;
+ ts = now - rrl->ts_bases[ts_gen];
+ if (ts < 0) {
+ if (ts < -DNS_RRL_MAX_TIME_TRAVEL)
+ ts = DNS_RRL_FOREVER;
+ else
+ ts = 0;
+ }
+
+ /*
+ * Make a new timestamp base if the current base is too old.
+ * All entries older than DNS_RRL_MAX_WINDOW seconds are ancient,
+ * useless history. Their timestamps can be treated as if they are
+ * all the same.
+ * We only do arithmetic on more recent timestamps, so bases for
+ * older timestamps can be recycled provided the old timestamps are
+ * marked as ancient history.
+ * This loop is almost always very short because most entries are
+ * recycled after one second and any entries that need to be marked
+ * are older than (DNS_RRL_TS_BASES)*DNS_RRL_MAX_TS seconds.
+ */
+ if (ts >= DNS_RRL_MAX_TS) {
+ ts_gen = (ts_gen + 1) % DNS_RRL_TS_BASES;
+ for (e_old = ISC_LIST_TAIL(rrl->lru), i = 0;
+ e_old != NULL && (e_old->ts_gen == ts_gen ||
+ !ISC_LINK_LINKED(e_old, hlink));
+ e_old = ISC_LIST_PREV(e_old, lru), ++i)
+ {
+ e_old->ts_valid = false;
+ }
+ if (i != 0)
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
+ DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_DEBUG1,
+ "rrl new time base scanned %d entries"
+ " at %d for %d %d %d %d",
+ i, now, rrl->ts_bases[ts_gen],
+ rrl->ts_bases[(ts_gen + 1) %
+ DNS_RRL_TS_BASES],
+ rrl->ts_bases[(ts_gen + 2) %
+ DNS_RRL_TS_BASES],
+ rrl->ts_bases[(ts_gen + 3) %
+ DNS_RRL_TS_BASES]);
+ rrl->ts_gen = ts_gen;
+ rrl->ts_bases[ts_gen] = now;
+ ts = 0;
+ }
+
+ e->ts_gen = ts_gen;
+ e->ts = ts;
+ e->ts_valid = true;
+}
+
+static isc_result_t
+expand_entries(dns_rrl_t *rrl, int newsize) {
+ unsigned int bsize;
+ dns_rrl_block_t *b;
+ dns_rrl_entry_t *e;
+ double rate;
+ int i;
+
+ if (rrl->num_entries + newsize >= rrl->max_entries &&
+ rrl->max_entries != 0)
+ {
+ newsize = rrl->max_entries - rrl->num_entries;
+ if (newsize <= 0)
+ return (ISC_R_SUCCESS);
+ }
+
+ /*
+ * Log expansions so that the user can tune max-table-size
+ * and min-table-size.
+ */
+ if (isc_log_wouldlog(dns_lctx, DNS_RRL_LOG_DROP) &&
+ rrl->hash != NULL) {
+ rate = rrl->probes;
+ if (rrl->searches != 0)
+ rate /= rrl->searches;
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
+ DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_DROP,
+ "increase from %d to %d RRL entries with"
+ " %d bins; average search length %.1f",
+ rrl->num_entries, rrl->num_entries+newsize,
+ rrl->hash->length, rate);
+ }
+
+ bsize = sizeof(dns_rrl_block_t) + (newsize-1)*sizeof(dns_rrl_entry_t);
+ b = isc_mem_get(rrl->mctx, bsize);
+ if (b == NULL) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
+ DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_FAIL,
+ "isc_mem_get(%d) failed for RRL entries",
+ bsize);
+ return (ISC_R_NOMEMORY);
+ }
+ memset(b, 0, bsize);
+ b->size = bsize;
+
+ e = b->entries;
+ for (i = 0; i < newsize; ++i, ++e) {
+ ISC_LINK_INIT(e, hlink);
+ ISC_LIST_INITANDAPPEND(rrl->lru, e, lru);
+ }
+ rrl->num_entries += newsize;
+ ISC_LIST_INITANDAPPEND(rrl->blocks, b, link);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline dns_rrl_bin_t *
+get_bin(dns_rrl_hash_t *hash, unsigned int hval) {
+ INSIST(hash != NULL);
+ return (&hash->bins[hval % hash->length]);
+}
+
+static void
+free_old_hash(dns_rrl_t *rrl) {
+ dns_rrl_hash_t *old_hash;
+ dns_rrl_bin_t *old_bin;
+ dns_rrl_entry_t *e, *e_next;
+
+ old_hash = rrl->old_hash;
+ for (old_bin = &old_hash->bins[0];
+ old_bin < &old_hash->bins[old_hash->length];
+ ++old_bin)
+ {
+ for (e = ISC_LIST_HEAD(*old_bin); e != NULL; e = e_next) {
+ e_next = ISC_LIST_NEXT(e, hlink);
+ ISC_LINK_INIT(e, hlink);
+ }
+ }
+
+ isc_mem_put(rrl->mctx, old_hash,
+ sizeof(*old_hash)
+ + (old_hash->length - 1) * sizeof(old_hash->bins[0]));
+ rrl->old_hash = NULL;
+}
+
+static isc_result_t
+expand_rrl_hash(dns_rrl_t *rrl, isc_stdtime_t now) {
+ dns_rrl_hash_t *hash;
+ int old_bins, new_bins, hsize;
+ double rate;
+
+ if (rrl->old_hash != NULL)
+ free_old_hash(rrl);
+
+ /*
+ * Most searches fail and so go to the end of the chain.
+ * Use a small hash table load factor.
+ */
+ old_bins = (rrl->hash == NULL) ? 0 : rrl->hash->length;
+ new_bins = old_bins/8 + old_bins;
+ if (new_bins < rrl->num_entries)
+ new_bins = rrl->num_entries;
+ new_bins = hash_divisor(new_bins);
+
+ hsize = sizeof(dns_rrl_hash_t) + (new_bins-1)*sizeof(hash->bins[0]);
+ hash = isc_mem_get(rrl->mctx, hsize);
+ if (hash == NULL) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
+ DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_FAIL,
+ "isc_mem_get(%d) failed for"
+ " RRL hash table",
+ hsize);
+ return (ISC_R_NOMEMORY);
+ }
+ memset(hash, 0, hsize);
+ hash->length = new_bins;
+ rrl->hash_gen ^= 1;
+ hash->gen = rrl->hash_gen;
+
+ if (isc_log_wouldlog(dns_lctx, DNS_RRL_LOG_DROP) && old_bins != 0) {
+ rate = rrl->probes;
+ if (rrl->searches != 0)
+ rate /= rrl->searches;
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
+ DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_DROP,
+ "increase from %d to %d RRL bins for"
+ " %d entries; average search length %.1f",
+ old_bins, new_bins, rrl->num_entries, rate);
+ }
+
+ rrl->old_hash = rrl->hash;
+ if (rrl->old_hash != NULL)
+ rrl->old_hash->check_time = now;
+ rrl->hash = hash;
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+ref_entry(dns_rrl_t *rrl, dns_rrl_entry_t *e, int probes, isc_stdtime_t now) {
+ /*
+ * Make the entry most recently used.
+ */
+ if (ISC_LIST_HEAD(rrl->lru) != e) {
+ if (e == rrl->last_logged)
+ rrl->last_logged = ISC_LIST_PREV(e, lru);
+ ISC_LIST_UNLINK(rrl->lru, e, lru);
+ ISC_LIST_PREPEND(rrl->lru, e, lru);
+ }
+
+ /*
+ * Expand the hash table if it is time and necessary.
+ * This will leave the newly referenced entry in a chain in the
+ * old hash table. It will migrate to the new hash table the next
+ * time it is used or be cut loose when the old hash table is destroyed.
+ */
+ rrl->probes += probes;
+ ++rrl->searches;
+ if (rrl->searches > 100 &&
+ delta_rrl_time(rrl->hash->check_time, now) > 1) {
+ if (rrl->probes/rrl->searches > 2)
+ expand_rrl_hash(rrl, now);
+ rrl->hash->check_time = now;
+ rrl->probes = 0;
+ rrl->searches = 0;
+ }
+}
+
+static inline bool
+key_cmp(const dns_rrl_key_t *a, const dns_rrl_key_t *b) {
+ if (memcmp(a, b, sizeof(dns_rrl_key_t)) == 0)
+ return (true);
+ return (false);
+}
+
+static inline uint32_t
+hash_key(const dns_rrl_key_t *key) {
+ uint32_t hval;
+ int i;
+
+ hval = key->w[0];
+ for (i = sizeof(key->w) / sizeof(key->w[0]) - 1; i >= 0; --i) {
+ hval = key->w[i] + (hval<<1);
+ }
+ return (hval);
+}
+
+/*
+ * Construct the hash table key.
+ * Use a hash of the DNS query name to save space in the database.
+ * Collisions result in legitimate rate limiting responses for one
+ * query name also limiting responses for other names to the
+ * same client. This is rare and benign enough given the large
+ * space costs compared to keeping the entire name in the database
+ * entry or the time costs of dynamic allocation.
+ */
+static void
+make_key(const dns_rrl_t *rrl, dns_rrl_key_t *key,
+ const isc_sockaddr_t *client_addr,
+ dns_rdatatype_t qtype, dns_name_t *qname, dns_rdataclass_t qclass,
+ dns_rrl_rtype_t rtype)
+{
+ dns_name_t base;
+ dns_offsets_t base_offsets;
+ int labels, i;
+
+ memset(key, 0, sizeof(*key));
+
+ key->s.rtype = rtype;
+ if (rtype == DNS_RRL_RTYPE_QUERY) {
+ key->s.qtype = qtype;
+ key->s.qclass = qclass & 0xff;
+ } else if (rtype == DNS_RRL_RTYPE_REFERRAL ||
+ rtype == DNS_RRL_RTYPE_NODATA) {
+ /*
+ * Because there is no qtype in the empty answer sections of
+ * referral and NODATA responses, count them as the same.
+ */
+ key->s.qclass = qclass & 0xff;
+ }
+
+ if (qname != NULL && qname->labels != 0) {
+ /*
+ * Ignore the first label of wildcards.
+ */
+ if ((qname->attributes & DNS_NAMEATTR_WILDCARD) != 0 &&
+ (labels = dns_name_countlabels(qname)) > 1)
+ {
+ dns_name_init(&base, base_offsets);
+ dns_name_getlabelsequence(qname, 1, labels-1, &base);
+ key->s.qname_hash =
+ dns_name_fullhash(&base, false);
+ } else {
+ key->s.qname_hash =
+ dns_name_fullhash(qname, false);
+ }
+ }
+
+ switch (client_addr->type.sa.sa_family) {
+ case AF_INET:
+ key->s.ip[0] = (client_addr->type.sin.sin_addr.s_addr &
+ rrl->ipv4_mask);
+ break;
+ case AF_INET6:
+ key->s.ipv6 = true;
+ memmove(key->s.ip, &client_addr->type.sin6.sin6_addr,
+ sizeof(key->s.ip));
+ for (i = 0; i < DNS_RRL_MAX_PREFIX/32; ++i)
+ key->s.ip[i] &= rrl->ipv6_mask[i];
+ break;
+ }
+}
+
+static inline dns_rrl_rate_t *
+get_rate(dns_rrl_t *rrl, dns_rrl_rtype_t rtype) {
+ switch (rtype) {
+ case DNS_RRL_RTYPE_QUERY:
+ return (&rrl->responses_per_second);
+ case DNS_RRL_RTYPE_REFERRAL:
+ return (&rrl->referrals_per_second);
+ case DNS_RRL_RTYPE_NODATA:
+ return (&rrl->nodata_per_second);
+ case DNS_RRL_RTYPE_NXDOMAIN:
+ return (&rrl->nxdomains_per_second);
+ case DNS_RRL_RTYPE_ERROR:
+ return (&rrl->errors_per_second);
+ case DNS_RRL_RTYPE_ALL:
+ return (&rrl->all_per_second);
+ default:
+ INSIST(0);
+ }
+ return (NULL);
+}
+
+static int
+response_balance(dns_rrl_t *rrl, const dns_rrl_entry_t *e, int age) {
+ dns_rrl_rate_t *ratep;
+ int balance, rate;
+
+ if (e->key.s.rtype == DNS_RRL_RTYPE_TCP) {
+ rate = 1;
+ } else {
+ ratep = get_rate(rrl, e->key.s.rtype);
+ rate = ratep->scaled;
+ }
+
+ balance = e->responses + age * rate;
+ if (balance > rate)
+ balance = rate;
+ return (balance);
+}
+
+/*
+ * Search for an entry for a response and optionally create it.
+ */
+static dns_rrl_entry_t *
+get_entry(dns_rrl_t *rrl, const isc_sockaddr_t *client_addr,
+ dns_rdataclass_t qclass, dns_rdatatype_t qtype, dns_name_t *qname,
+ dns_rrl_rtype_t rtype, isc_stdtime_t now, bool create,
+ char *log_buf, unsigned int log_buf_len)
+{
+ dns_rrl_key_t key;
+ uint32_t hval;
+ dns_rrl_entry_t *e;
+ dns_rrl_hash_t *hash;
+ dns_rrl_bin_t *new_bin, *old_bin;
+ int probes, age;
+
+ make_key(rrl, &key, client_addr, qtype, qname, qclass, rtype);
+ hval = hash_key(&key);
+
+ /*
+ * Look for the entry in the current hash table.
+ */
+ new_bin = get_bin(rrl->hash, hval);
+ probes = 1;
+ e = ISC_LIST_HEAD(*new_bin);
+ while (e != NULL) {
+ if (key_cmp(&e->key, &key)) {
+ ref_entry(rrl, e, probes, now);
+ return (e);
+ }
+ ++probes;
+ e = ISC_LIST_NEXT(e, hlink);
+ }
+
+ /*
+ * Look in the old hash table.
+ */
+ if (rrl->old_hash != NULL) {
+ old_bin = get_bin(rrl->old_hash, hval);
+ e = ISC_LIST_HEAD(*old_bin);
+ while (e != NULL) {
+ if (key_cmp(&e->key, &key)) {
+ ISC_LIST_UNLINK(*old_bin, e, hlink);
+ ISC_LIST_PREPEND(*new_bin, e, hlink);
+ e->hash_gen = rrl->hash_gen;
+ ref_entry(rrl, e, probes, now);
+ return (e);
+ }
+ e = ISC_LIST_NEXT(e, hlink);
+ }
+
+ /*
+ * Discard prevous hash table when all of its entries are old.
+ */
+ age = delta_rrl_time(rrl->old_hash->check_time, now);
+ if (age > rrl->window)
+ free_old_hash(rrl);
+ }
+
+ if (!create)
+ return (NULL);
+
+ /*
+ * The entry does not exist, so create it by finding a free entry.
+ * Keep currently penalized and logged entries.
+ * Try to make more entries if none are idle.
+ * Steal the oldest entry if we cannot create more.
+ */
+ for (e = ISC_LIST_TAIL(rrl->lru);
+ e != NULL;
+ e = ISC_LIST_PREV(e, lru))
+ {
+ if (!ISC_LINK_LINKED(e, hlink))
+ break;
+ age = get_age(rrl, e, now);
+ if (age <= 1) {
+ e = NULL;
+ break;
+ }
+ if (!e->logged && response_balance(rrl, e, age) > 0)
+ break;
+ }
+ if (e == NULL) {
+ expand_entries(rrl, ISC_MIN((rrl->num_entries+1)/2, 1000));
+ e = ISC_LIST_TAIL(rrl->lru);
+ }
+ if (e->logged)
+ log_end(rrl, e, true, log_buf, log_buf_len);
+ if (ISC_LINK_LINKED(e, hlink)) {
+ if (e->hash_gen == rrl->hash_gen)
+ hash = rrl->hash;
+ else
+ hash = rrl->old_hash;
+ old_bin = get_bin(hash, hash_key(&e->key));
+ ISC_LIST_UNLINK(*old_bin, e, hlink);
+ }
+ ISC_LIST_PREPEND(*new_bin, e, hlink);
+ e->hash_gen = rrl->hash_gen;
+ e->key = key;
+ e->ts_valid = false;
+ ref_entry(rrl, e, probes, now);
+ return (e);
+}
+
+static void
+debit_log(const dns_rrl_entry_t *e, int age, const char *action) {
+ char buf[sizeof("age=12345678")];
+ const char *age_str;
+
+ if (age == DNS_RRL_FOREVER) {
+ age_str = "";
+ } else {
+ snprintf(buf, sizeof(buf), "age=%d", age);
+ age_str = buf;
+ }
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
+ DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_DEBUG3,
+ "rrl %08x %6s responses=%-3d %s",
+ hash_key(&e->key), age_str, e->responses, action);
+}
+
+static inline dns_rrl_result_t
+debit_rrl_entry(dns_rrl_t *rrl, dns_rrl_entry_t *e, double qps, double scale,
+ const isc_sockaddr_t *client_addr, isc_stdtime_t now,
+ char *log_buf, unsigned int log_buf_len)
+{
+ int rate, new_rate, slip, new_slip, age, log_secs, min;
+ dns_rrl_rate_t *ratep;
+ dns_rrl_entry_t const *credit_e;
+
+ /*
+ * Pick the rate counter.
+ * Optionally adjust the rate by the estimated query/second rate.
+ */
+ ratep = get_rate(rrl, e->key.s.rtype);
+ rate = ratep->r;
+ if (rate == 0)
+ return (DNS_RRL_RESULT_OK);
+
+ if (scale < 1.0) {
+ /*
+ * The limit for clients that have used TCP is not scaled.
+ */
+ credit_e = get_entry(rrl, client_addr,
+ 0, dns_rdatatype_none, NULL,
+ DNS_RRL_RTYPE_TCP, now, false,
+ log_buf, log_buf_len);
+ if (credit_e != NULL) {
+ age = get_age(rrl, e, now);
+ if (age < rrl->window)
+ scale = 1.0;
+ }
+ }
+ if (scale < 1.0) {
+ new_rate = (int) (rate * scale);
+ if (new_rate < 1)
+ new_rate = 1;
+ if (ratep->scaled != new_rate) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
+ DNS_LOGMODULE_REQUEST,
+ DNS_RRL_LOG_DEBUG1,
+ "%d qps scaled %s by %.2f"
+ " from %d to %d",
+ (int)qps, ratep->str, scale,
+ rate, new_rate);
+ rate = new_rate;
+ ratep->scaled = rate;
+ }
+ }
+
+ min = -rrl->window * rate;
+
+ /*
+ * Treat time jumps into the recent past as no time.
+ * Treat entries older than the window as if they were just created
+ * Credit other entries.
+ */
+ age = get_age(rrl, e, now);
+ if (age > 0) {
+ /*
+ * Credit tokens earned during elapsed time.
+ */
+ if (age > rrl->window) {
+ e->responses = rate;
+ e->slip_cnt = 0;
+ } else {
+ e->responses += rate*age;
+ if (e->responses > rate) {
+ e->responses = rate;
+ e->slip_cnt = 0;
+ }
+ }
+ /*
+ * Find the seconds since last log message without overflowing
+ * small counter. This counter is reset when an entry is
+ * created. It is not necessarily reset when some requests
+ * are answered provided other requests continue to be dropped
+ * or slipped. This can happen when the request rate is just
+ * at the limit.
+ */
+ if (e->logged) {
+ log_secs = e->log_secs;
+ log_secs += age;
+ if (log_secs > DNS_RRL_MAX_LOG_SECS || log_secs < 0)
+ log_secs = DNS_RRL_MAX_LOG_SECS;
+ e->log_secs = log_secs;
+ }
+ }
+ set_age(rrl, e, now);
+
+ /*
+ * Debit the entry for this response.
+ */
+ if (--e->responses >= 0) {
+ if (isc_log_wouldlog(dns_lctx, DNS_RRL_LOG_DEBUG3))
+ debit_log(e, age, "");
+ return (DNS_RRL_RESULT_OK);
+ }
+
+ if (e->responses < min)
+ e->responses = min;
+
+ /*
+ * Drop this response unless it should slip or leak.
+ */
+ slip = rrl->slip.r;
+ if (slip > 2 && scale < 1.0) {
+ new_slip = (int) (slip * scale);
+ if (new_slip < 2)
+ new_slip = 2;
+ if (rrl->slip.scaled != new_slip) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
+ DNS_LOGMODULE_REQUEST,
+ DNS_RRL_LOG_DEBUG1,
+ "%d qps scaled slip"
+ " by %.2f from %d to %d",
+ (int)qps, scale,
+ slip, new_slip);
+ slip = new_slip;
+ rrl->slip.scaled = slip;
+ }
+ }
+ if (slip != 0 && e->key.s.rtype != DNS_RRL_RTYPE_ALL) {
+ if (e->slip_cnt++ == 0) {
+ if ((int) e->slip_cnt >= slip)
+ e->slip_cnt = 0;
+ if (isc_log_wouldlog(dns_lctx, DNS_RRL_LOG_DEBUG3))
+ debit_log(e, age, "slip");
+ return (DNS_RRL_RESULT_SLIP);
+ } else if ((int) e->slip_cnt >= slip) {
+ e->slip_cnt = 0;
+ }
+ }
+
+ if (isc_log_wouldlog(dns_lctx, DNS_RRL_LOG_DEBUG3))
+ debit_log(e, age, "drop");
+ return (DNS_RRL_RESULT_DROP);
+}
+
+static inline dns_rrl_qname_buf_t *
+get_qname(dns_rrl_t *rrl, const dns_rrl_entry_t *e) {
+ dns_rrl_qname_buf_t *qbuf;
+
+ qbuf = rrl->qnames[e->log_qname];
+ if (qbuf == NULL || qbuf->e != e)
+ return (NULL);
+ return (qbuf);
+}
+
+static inline void
+free_qname(dns_rrl_t *rrl, dns_rrl_entry_t *e) {
+ dns_rrl_qname_buf_t *qbuf;
+
+ qbuf = get_qname(rrl, e);
+ if (qbuf != NULL) {
+ qbuf->e = NULL;
+ ISC_LIST_APPEND(rrl->qname_free, qbuf, link);
+ }
+}
+
+static void
+add_log_str(isc_buffer_t *lb, const char *str, unsigned int str_len) {
+ isc_region_t region;
+
+ isc_buffer_availableregion(lb, &region);
+ if (str_len >= region.length) {
+ if (region.length == 0U)
+ return;
+ str_len = region.length;
+ }
+ memmove(region.base, str, str_len);
+ isc_buffer_add(lb, str_len);
+}
+
+#define ADD_LOG_CSTR(eb, s) add_log_str(eb, s, sizeof(s)-1)
+
+/*
+ * Build strings for the logs
+ */
+static void
+make_log_buf(dns_rrl_t *rrl, dns_rrl_entry_t *e,
+ const char *str1, const char *str2, bool plural,
+ dns_name_t *qname, bool save_qname,
+ dns_rrl_result_t rrl_result, isc_result_t resp_result,
+ char *log_buf, unsigned int log_buf_len)
+{
+ isc_buffer_t lb;
+ dns_rrl_qname_buf_t *qbuf;
+ isc_netaddr_t cidr;
+ char strbuf[ISC_MAX(sizeof("/123"), sizeof(" (12345678)"))];
+ const char *rstr;
+ isc_result_t msg_result;
+
+ if (log_buf_len <= 1) {
+ if (log_buf_len == 1)
+ log_buf[0] = '\0';
+ return;
+ }
+ isc_buffer_init(&lb, log_buf, log_buf_len-1);
+
+ if (str1 != NULL)
+ add_log_str(&lb, str1, strlen(str1));
+ if (str2 != NULL)
+ add_log_str(&lb, str2, strlen(str2));
+
+ switch (rrl_result) {
+ case DNS_RRL_RESULT_OK:
+ break;
+ case DNS_RRL_RESULT_DROP:
+ ADD_LOG_CSTR(&lb, "drop ");
+ break;
+ case DNS_RRL_RESULT_SLIP:
+ ADD_LOG_CSTR(&lb, "slip ");
+ break;
+ default:
+ INSIST(0);
+ break;
+ }
+
+ switch (e->key.s.rtype) {
+ case DNS_RRL_RTYPE_QUERY:
+ break;
+ case DNS_RRL_RTYPE_REFERRAL:
+ ADD_LOG_CSTR(&lb, "referral ");
+ break;
+ case DNS_RRL_RTYPE_NODATA:
+ ADD_LOG_CSTR(&lb, "NODATA ");
+ break;
+ case DNS_RRL_RTYPE_NXDOMAIN:
+ ADD_LOG_CSTR(&lb, "NXDOMAIN ");
+ break;
+ case DNS_RRL_RTYPE_ERROR:
+ if (resp_result == ISC_R_SUCCESS) {
+ ADD_LOG_CSTR(&lb, "error ");
+ } else {
+ rstr = isc_result_totext(resp_result);
+ add_log_str(&lb, rstr, strlen(rstr));
+ ADD_LOG_CSTR(&lb, " error ");
+ }
+ break;
+ case DNS_RRL_RTYPE_ALL:
+ ADD_LOG_CSTR(&lb, "all ");
+ break;
+ default:
+ INSIST(0);
+ }
+
+ if (plural)
+ ADD_LOG_CSTR(&lb, "responses to ");
+ else
+ ADD_LOG_CSTR(&lb, "response to ");
+
+ memset(&cidr, 0, sizeof(cidr));
+ if (e->key.s.ipv6) {
+ snprintf(strbuf, sizeof(strbuf), "/%d", rrl->ipv6_prefixlen);
+ cidr.family = AF_INET6;
+ memset(&cidr.type.in6, 0, sizeof(cidr.type.in6));
+ memmove(&cidr.type.in6, e->key.s.ip, sizeof(e->key.s.ip));
+ } else {
+ snprintf(strbuf, sizeof(strbuf), "/%d", rrl->ipv4_prefixlen);
+ cidr.family = AF_INET;
+ cidr.type.in.s_addr = e->key.s.ip[0];
+ }
+ msg_result = isc_netaddr_totext(&cidr, &lb);
+ if (msg_result != ISC_R_SUCCESS)
+ ADD_LOG_CSTR(&lb, "?");
+ add_log_str(&lb, strbuf, strlen(strbuf));
+
+ if (e->key.s.rtype == DNS_RRL_RTYPE_QUERY ||
+ e->key.s.rtype == DNS_RRL_RTYPE_REFERRAL ||
+ e->key.s.rtype == DNS_RRL_RTYPE_NODATA ||
+ e->key.s.rtype == DNS_RRL_RTYPE_NXDOMAIN) {
+ qbuf = get_qname(rrl, e);
+ if (save_qname && qbuf == NULL &&
+ qname != NULL && dns_name_isabsolute(qname)) {
+ /*
+ * Capture the qname for the "stop limiting" message.
+ */
+ qbuf = ISC_LIST_TAIL(rrl->qname_free);
+ if (qbuf != NULL) {
+ ISC_LIST_UNLINK(rrl->qname_free, qbuf, link);
+ } else if (rrl->num_qnames < DNS_RRL_QNAMES) {
+ qbuf = isc_mem_get(rrl->mctx, sizeof(*qbuf));
+ if (qbuf != NULL) {
+ memset(qbuf, 0, sizeof(*qbuf));
+ ISC_LINK_INIT(qbuf, link);
+ qbuf->index = rrl->num_qnames;
+ rrl->qnames[rrl->num_qnames++] = qbuf;
+ } else {
+ isc_log_write(dns_lctx,
+ DNS_LOGCATEGORY_RRL,
+ DNS_LOGMODULE_REQUEST,
+ DNS_RRL_LOG_FAIL,
+ "isc_mem_get(%d)"
+ " failed for RRL qname",
+ (int)sizeof(*qbuf));
+ }
+ }
+ if (qbuf != NULL) {
+ e->log_qname = qbuf->index;
+ qbuf->e = e;
+ dns_fixedname_init(&qbuf->qname);
+ dns_name_copy(qname,
+ dns_fixedname_name(&qbuf->qname),
+ NULL);
+ }
+ }
+ if (qbuf != NULL)
+ qname = dns_fixedname_name(&qbuf->qname);
+ if (qname != NULL) {
+ ADD_LOG_CSTR(&lb, " for ");
+ (void)dns_name_totext(qname, true, &lb);
+ } else {
+ ADD_LOG_CSTR(&lb, " for (?)");
+ }
+ if (e->key.s.rtype != DNS_RRL_RTYPE_NXDOMAIN) {
+ ADD_LOG_CSTR(&lb, " ");
+ (void)dns_rdataclass_totext(e->key.s.qclass, &lb);
+ if (e->key.s.rtype == DNS_RRL_RTYPE_QUERY) {
+ ADD_LOG_CSTR(&lb, " ");
+ (void)dns_rdatatype_totext(e->key.s.qtype, &lb);
+ }
+ }
+ snprintf(strbuf, sizeof(strbuf), " (%08x)",
+ e->key.s.qname_hash);
+ add_log_str(&lb, strbuf, strlen(strbuf));
+ }
+
+ /*
+ * We saved room for '\0'.
+ */
+ log_buf[isc_buffer_usedlength(&lb)] = '\0';
+}
+
+static void
+log_end(dns_rrl_t *rrl, dns_rrl_entry_t *e, bool early,
+ char *log_buf, unsigned int log_buf_len)
+{
+ if (e->logged) {
+ make_log_buf(rrl, e,
+ early ? "*" : NULL,
+ rrl->log_only ? "would stop limiting "
+ : "stop limiting ",
+ true, NULL, false,
+ DNS_RRL_RESULT_OK, ISC_R_SUCCESS,
+ log_buf, log_buf_len);
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
+ DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_DROP,
+ "%s", log_buf);
+ free_qname(rrl, e);
+ e->logged = false;
+ --rrl->num_logged;
+ }
+}
+
+/*
+ * Log messages for streams that have stopped being rate limited.
+ */
+static void
+log_stops(dns_rrl_t *rrl, isc_stdtime_t now, int limit,
+ char *log_buf, unsigned int log_buf_len)
+{
+ dns_rrl_entry_t *e;
+ int age;
+
+ for (e = rrl->last_logged; e != NULL; e = ISC_LIST_PREV(e, lru)) {
+ if (!e->logged)
+ continue;
+ if (now != 0) {
+ age = get_age(rrl, e, now);
+ if (age < DNS_RRL_STOP_LOG_SECS ||
+ response_balance(rrl, e, age) < 0)
+ break;
+ }
+
+ log_end(rrl, e, now == 0, log_buf, log_buf_len);
+ if (rrl->num_logged <= 0)
+ break;
+
+ /*
+ * Too many messages could stall real work.
+ */
+ if (--limit < 0) {
+ rrl->last_logged = ISC_LIST_PREV(e, lru);
+ return;
+ }
+ }
+ if (e == NULL) {
+ INSIST(rrl->num_logged == 0);
+ rrl->log_stops_time = now;
+ }
+ rrl->last_logged = e;
+}
+
+/*
+ * Main rate limit interface.
+ */
+dns_rrl_result_t
+dns_rrl(dns_view_t *view,
+ const isc_sockaddr_t *client_addr, bool is_tcp,
+ dns_rdataclass_t qclass, dns_rdatatype_t qtype,
+ dns_name_t *qname, isc_result_t resp_result, isc_stdtime_t now,
+ bool wouldlog, char *log_buf, unsigned int log_buf_len)
+{
+ dns_rrl_t *rrl;
+ dns_rrl_rtype_t rtype;
+ dns_rrl_entry_t *e;
+ isc_netaddr_t netclient;
+ int secs;
+ double qps, scale;
+ int exempt_match;
+ isc_result_t result;
+ dns_rrl_result_t rrl_result;
+
+ INSIST(log_buf != NULL && log_buf_len > 0);
+
+ rrl = view->rrl;
+ if (rrl->exempt != NULL) {
+ isc_netaddr_fromsockaddr(&netclient, client_addr);
+ result = dns_acl_match(&netclient, NULL, rrl->exempt,
+ &view->aclenv, &exempt_match, NULL);
+ if (result == ISC_R_SUCCESS && exempt_match > 0)
+ return (DNS_RRL_RESULT_OK);
+ }
+
+ LOCK(&rrl->lock);
+
+ /*
+ * Estimate total query per second rate when scaling by qps.
+ */
+ if (rrl->qps_scale == 0) {
+ qps = 0.0;
+ scale = 1.0;
+ } else {
+ ++rrl->qps_responses;
+ secs = delta_rrl_time(rrl->qps_time, now);
+ if (secs <= 0) {
+ qps = rrl->qps;
+ } else {
+ qps = (1.0*rrl->qps_responses) / secs;
+ if (secs >= rrl->window) {
+ if (isc_log_wouldlog(dns_lctx,
+ DNS_RRL_LOG_DEBUG3))
+ isc_log_write(dns_lctx,
+ DNS_LOGCATEGORY_RRL,
+ DNS_LOGMODULE_REQUEST,
+ DNS_RRL_LOG_DEBUG3,
+ "%d responses/%d seconds"
+ " = %d qps",
+ rrl->qps_responses, secs,
+ (int)qps);
+ rrl->qps = qps;
+ rrl->qps_responses = 0;
+ rrl->qps_time = now;
+ } else if (qps < rrl->qps) {
+ qps = rrl->qps;
+ }
+ }
+ scale = rrl->qps_scale / qps;
+ }
+
+ /*
+ * Do maintenance once per second.
+ */
+ if (rrl->num_logged > 0 && rrl->log_stops_time != now)
+ log_stops(rrl, now, 8, log_buf, log_buf_len);
+
+ /*
+ * Notice TCP responses when scaling limits by qps.
+ * Do not try to rate limit TCP responses.
+ */
+ if (is_tcp) {
+ if (scale < 1.0) {
+ e = get_entry(rrl, client_addr,
+ 0, dns_rdatatype_none, NULL,
+ DNS_RRL_RTYPE_TCP, now, true,
+ log_buf, log_buf_len);
+ if (e != NULL) {
+ e->responses = -(rrl->window+1);
+ set_age(rrl, e, now);
+ }
+ }
+ UNLOCK(&rrl->lock);
+ return (ISC_R_SUCCESS);
+ }
+
+ /*
+ * Find the right kind of entry, creating it if necessary.
+ * If that is impossible, then nothing more can be done
+ */
+ switch (resp_result) {
+ case ISC_R_SUCCESS:
+ rtype = DNS_RRL_RTYPE_QUERY;
+ break;
+ case DNS_R_DELEGATION:
+ rtype = DNS_RRL_RTYPE_REFERRAL;
+ break;
+ case DNS_R_NXRRSET:
+ rtype = DNS_RRL_RTYPE_NODATA;
+ break;
+ case DNS_R_NXDOMAIN:
+ rtype = DNS_RRL_RTYPE_NXDOMAIN;
+ break;
+ default:
+ rtype = DNS_RRL_RTYPE_ERROR;
+ break;
+ }
+ e = get_entry(rrl, client_addr, qclass, qtype, qname, rtype,
+ now, true, log_buf, log_buf_len);
+ if (e == NULL) {
+ UNLOCK(&rrl->lock);
+ return (DNS_RRL_RESULT_OK);
+ }
+
+ if (isc_log_wouldlog(dns_lctx, DNS_RRL_LOG_DEBUG1)) {
+ /*
+ * Do not worry about speed or releasing the lock.
+ * This message appears before messages from debit_rrl_entry().
+ */
+ make_log_buf(rrl, e, "consider limiting ", NULL, false,
+ qname, false, DNS_RRL_RESULT_OK, resp_result,
+ log_buf, log_buf_len);
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
+ DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_DEBUG1,
+ "%s", log_buf);
+ }
+
+ rrl_result = debit_rrl_entry(rrl, e, qps, scale, client_addr, now,
+ log_buf, log_buf_len);
+
+ if (rrl->all_per_second.r != 0) {
+ /*
+ * We must debit the all-per-second token bucket if we have
+ * an all-per-second limit for the IP address.
+ * The all-per-second limit determines the log message
+ * when both limits are hit.
+ * The response limiting must continue if the
+ * all-per-second limiting lapses.
+ */
+ dns_rrl_entry_t *e_all;
+ dns_rrl_result_t rrl_all_result;
+
+ e_all = get_entry(rrl, client_addr,
+ 0, dns_rdatatype_none, NULL,
+ DNS_RRL_RTYPE_ALL, now, true,
+ log_buf, log_buf_len);
+ if (e_all == NULL) {
+ UNLOCK(&rrl->lock);
+ return (DNS_RRL_RESULT_OK);
+ }
+ rrl_all_result = debit_rrl_entry(rrl, e_all, qps, scale,
+ client_addr, now,
+ log_buf, log_buf_len);
+ if (rrl_all_result != DNS_RRL_RESULT_OK) {
+ e = e_all;
+ rrl_result = rrl_all_result;
+ if (isc_log_wouldlog(dns_lctx, DNS_RRL_LOG_DEBUG1)) {
+ make_log_buf(rrl, e,
+ "prefer all-per-second limiting ",
+ NULL, true, qname, false,
+ DNS_RRL_RESULT_OK, resp_result,
+ log_buf, log_buf_len);
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
+ DNS_LOGMODULE_REQUEST,
+ DNS_RRL_LOG_DEBUG1,
+ "%s", log_buf);
+ }
+ }
+ }
+
+ if (rrl_result == DNS_RRL_RESULT_OK) {
+ UNLOCK(&rrl->lock);
+ return (DNS_RRL_RESULT_OK);
+ }
+
+ /*
+ * Log occassionally in the rate-limit category.
+ */
+ if ((!e->logged || e->log_secs >= DNS_RRL_MAX_LOG_SECS) &&
+ isc_log_wouldlog(dns_lctx, DNS_RRL_LOG_DROP)) {
+ make_log_buf(rrl, e, rrl->log_only ? "would " : NULL,
+ e->logged ? "continue limiting " : "limit ",
+ true, qname, true,
+ DNS_RRL_RESULT_OK, resp_result,
+ log_buf, log_buf_len);
+ if (!e->logged) {
+ e->logged = true;
+ if (++rrl->num_logged <= 1)
+ rrl->last_logged = e;
+ }
+ e->log_secs = 0;
+
+ /*
+ * Avoid holding the lock.
+ */
+ if (!wouldlog) {
+ UNLOCK(&rrl->lock);
+ e = NULL;
+ }
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
+ DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_DROP,
+ "%s", log_buf);
+ }
+
+ /*
+ * Make a log message for the caller.
+ */
+ if (wouldlog)
+ make_log_buf(rrl, e,
+ rrl->log_only ? "would rate limit " : "rate limit ",
+ NULL, false, qname, false,
+ rrl_result, resp_result, log_buf, log_buf_len);
+
+ if (e != NULL) {
+ /*
+ * Do not save the qname unless we might need it for
+ * the ending log message.
+ */
+ if (!e->logged)
+ free_qname(rrl, e);
+ UNLOCK(&rrl->lock);
+ }
+
+ return (rrl_result);
+}
+
+void
+dns_rrl_view_destroy(dns_view_t *view) {
+ dns_rrl_t *rrl;
+ dns_rrl_block_t *b;
+ dns_rrl_hash_t *h;
+ char log_buf[DNS_RRL_LOG_BUF_LEN];
+ int i;
+
+ rrl = view->rrl;
+ if (rrl == NULL)
+ return;
+ view->rrl = NULL;
+
+ /*
+ * Assume the caller takes care of locking the view and anything else.
+ */
+
+ if (rrl->num_logged > 0)
+ log_stops(rrl, 0, INT32_MAX, log_buf, sizeof(log_buf));
+
+ for (i = 0; i < DNS_RRL_QNAMES; ++i) {
+ if (rrl->qnames[i] == NULL)
+ break;
+ isc_mem_put(rrl->mctx, rrl->qnames[i], sizeof(*rrl->qnames[i]));
+ }
+
+ if (rrl->exempt != NULL)
+ dns_acl_detach(&rrl->exempt);
+
+ DESTROYLOCK(&rrl->lock);
+
+ while (!ISC_LIST_EMPTY(rrl->blocks)) {
+ b = ISC_LIST_HEAD(rrl->blocks);
+ ISC_LIST_UNLINK(rrl->blocks, b, link);
+ isc_mem_put(rrl->mctx, b, b->size);
+ }
+
+ h = rrl->hash;
+ if (h != NULL)
+ isc_mem_put(rrl->mctx, h,
+ sizeof(*h) + (h->length - 1) * sizeof(h->bins[0]));
+
+ h = rrl->old_hash;
+ if (h != NULL)
+ isc_mem_put(rrl->mctx, h,
+ sizeof(*h) + (h->length - 1) * sizeof(h->bins[0]));
+
+ isc_mem_putanddetach(&rrl->mctx, rrl, sizeof(*rrl));
+}
+
+isc_result_t
+dns_rrl_init(dns_rrl_t **rrlp, dns_view_t *view, int min_entries) {
+ dns_rrl_t *rrl;
+ isc_result_t result;
+
+ *rrlp = NULL;
+
+ rrl = isc_mem_get(view->mctx, sizeof(*rrl));
+ if (rrl == NULL)
+ return (ISC_R_NOMEMORY);
+ memset(rrl, 0, sizeof(*rrl));
+ isc_mem_attach(view->mctx, &rrl->mctx);
+ result = isc_mutex_init(&rrl->lock);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_putanddetach(&rrl->mctx, rrl, sizeof(*rrl));
+ return (result);
+ }
+ isc_stdtime_get(&rrl->ts_bases[0]);
+
+ view->rrl = rrl;
+
+ result = expand_entries(rrl, min_entries);
+ if (result != ISC_R_SUCCESS) {
+ dns_rrl_view_destroy(view);
+ return (result);
+ }
+ result = expand_rrl_hash(rrl, 0);
+ if (result != ISC_R_SUCCESS) {
+ dns_rrl_view_destroy(view);
+ return (result);
+ }
+
+ *rrlp = rrl;
+ return (ISC_R_SUCCESS);
+}
diff --git a/lib/dns/sdb.c b/lib/dns/sdb.c
new file mode 100644
index 0000000..cabda7f
--- /dev/null
+++ b/lib/dns/sdb.c
@@ -0,0 +1,1593 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.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 28800U /* 8 hours */
+#define SDB_DEFAULT_RETRY 7200U /* 2 hours */
+#define SDB_DEFAULT_EXPIRE 604800U /* 7 days */
+#define SDB_DEFAULT_MINIMUM 86400U /* 1 day */
+
+/* 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 || methods->lookup2 != NULL);
+ REQUIRE(mctx != NULL);
+ REQUIRE(sdbimp != NULL && *sdbimp == NULL);
+ REQUIRE((flags & ~(DNS_SDBFLAG_RELATIVEOWNER |
+ DNS_SDBFLAG_RELATIVERDATA |
+ DNS_SDBFLAG_THREADSAFE|
+ DNS_SDBFLAG_DNS64)) == 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);
+ dns_rdatalist_init(rdatalist);
+ rdatalist->rdclass = lookup->sdb->common.rdclass;
+ rdatalist->type = typeval;
+ rdatalist->ttl = ttl;
+ 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_constinit(&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;
+
+ newname = dns_fixedname_initname(&fnewname);
+
+ if ((imp->flags & DNS_SDBFLAG_RELATIVERDATA) != 0)
+ origin = &sdb->common.origin;
+ else
+ origin = dns_rootname;
+ isc_buffer_constinit(&b, name, strlen(name));
+ isc_buffer_add(&b, strlen(name));
+
+ result = dns_name_fromtext(newname, &b, origin, 0, 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,
+ 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);
+ bool need_destroy = false;
+
+ REQUIRE(VALID_SDB(sdb));
+ LOCK(&sdb->lock);
+ REQUIRE(sdb->references > 0);
+ sdb->references--;
+ if (sdb->references == 0)
+ need_destroy = true;
+ UNLOCK(&sdb->lock);
+
+ if (need_destroy)
+ destroy(sdb);
+
+ *dbp = NULL;
+}
+
+static isc_result_t
+beginload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
+ UNUSED(db);
+ UNUSED(callbacks);
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static isc_result_t
+endload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
+ UNUSED(db);
+ UNUSED(callbacks);
+ 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, bool commit) {
+ REQUIRE(versionp != NULL && *versionp == (void *) &dummy);
+ REQUIRE(commit == 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
+findnodeext(dns_db_t *db, dns_name_t *name, bool create,
+ dns_clientinfomethods_t *methods, dns_clientinfo_t *clientinfo,
+ 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];
+ bool isorigin;
+ dns_sdbimplementation_t *imp;
+ dns_name_t relname;
+ unsigned int labels;
+
+ REQUIRE(VALID_SDB(sdb));
+ REQUIRE(create == false);
+ REQUIRE(nodep != NULL && *nodep == NULL);
+
+ UNUSED(name);
+ UNUSED(create);
+
+ imp = sdb->implementation;
+
+ isorigin = dns_name_equal(name, &sdb->common.origin);
+
+ if (imp->methods->lookup2 != NULL) {
+ if ((imp->flags & DNS_SDBFLAG_RELATIVEOWNER) != 0) {
+ labels = dns_name_countlabels(name) -
+ dns_name_countlabels(&db->origin);
+ dns_name_init(&relname, NULL);
+ dns_name_getlabelsequence(name, 0, labels, &relname);
+ name = &relname;
+ }
+ } else {
+ isc_buffer_init(&b, namestr, sizeof(namestr));
+ if ((imp->flags & DNS_SDBFLAG_RELATIVEOWNER) != 0) {
+
+ 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, true, &b);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ } else {
+ result = dns_name_totext(name, 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);
+
+ MAYBE_LOCK(sdb);
+ if (imp->methods->lookup2 != NULL)
+ result = imp->methods->lookup2(&sdb->common.origin, name,
+ sdb->dbdata, node, methods,
+ clientinfo);
+ else
+ result = imp->methods->lookup(sdb->zone, namestr, sdb->dbdata,
+ node, methods, clientinfo);
+ 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
+findext(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_clientinfomethods_t *methods, dns_clientinfo_t *clientinfo,
+ 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;
+ unsigned int flags;
+
+ REQUIRE(VALID_SDB(sdb));
+ REQUIRE(nodep == NULL || *nodep == NULL);
+ REQUIRE(version == NULL || version == (void *) &dummy);
+
+ UNUSED(options);
+
+ if (!dns_name_issubdomain(name, &db->origin))
+ return (DNS_R_NXDOMAIN);
+
+ olabels = dns_name_countlabels(&db->origin);
+ nlabels = dns_name_countlabels(name);
+
+ xname = dns_fixedname_initname(&fname);
+
+ if (rdataset == NULL) {
+ dns_rdataset_init(&xrdataset);
+ rdataset = &xrdataset;
+ }
+
+ result = DNS_R_NXDOMAIN;
+ flags = sdb->implementation->flags;
+ i = (flags & DNS_SDBFLAG_DNS64) != 0 ? nlabels : olabels;
+ for (; i <= nlabels; i++) {
+ /*
+ * Look up the next label.
+ */
+ dns_name_getlabelsequence(name, nlabels - i, i, xname);
+ result = findnodeext(db, xname, false, methods,
+ clientinfo, &node);
+ if (result == ISC_R_NOTFOUND) {
+ /*
+ * No data at zone apex?
+ */
+ if (i == olabels)
+ return (DNS_R_BADDB);
+ result = DNS_R_NXDOMAIN;
+ continue;
+ }
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ /*
+ * DNS64 zone's don't have DNAME or NS records.
+ */
+ if ((flags & DNS_SDBFLAG_DNS64) != 0)
+ goto skip;
+
+ /*
+ * DNS64 zone's don't have DNAME or NS records.
+ */
+ if ((flags & DNS_SDBFLAG_DNS64) != 0)
+ goto skip;
+
+ /*
+ * 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_isassociated
+ (sigrdataset)) {
+ 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;
+ }
+
+ skip:
+ /*
+ * 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;
+ bool need_destroy = 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 = 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 = (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 bool
+issecure(dns_db_t *db) {
+ UNUSED(db);
+
+ return (false);
+}
+
+static unsigned int
+nodecount(dns_db_t *db) {
+ UNUSED(db);
+
+ return (0);
+}
+
+static bool
+ispersistent(dns_db_t *db) {
+ UNUSED(db);
+ return (true);
+}
+
+static void
+overmem(dns_db_t *db, bool over) {
+ UNUSED(db);
+ UNUSED(over);
+}
+
+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,
+ NULL,
+ dump,
+ currentversion,
+ newversion,
+ attachversion,
+ closeversion,
+ NULL,
+ NULL,
+ findzonecut,
+ attachnode,
+ detachnode,
+ expirenode,
+ printnode,
+ createiterator,
+ findrdataset,
+ allrdatasets,
+ addrdataset,
+ subtractrdataset,
+ deleterdataset,
+ issecure,
+ nodecount,
+ ispersistent,
+ overmem,
+ settask,
+ NULL, /* getoriginnode */
+ NULL, /* transfernode */
+ NULL, /* getnsec3parameters */
+ NULL, /* findnsec3node */
+ NULL, /* setsigningtime */
+ NULL, /* getsigningtime */
+ NULL, /* resigned */
+ NULL, /* isdnssec */
+ NULL, /* getrrsetstats */
+ NULL, /* rpz_attach */
+ NULL, /* rpz_ready */
+ findnodeext,
+ findext,
+ NULL, /* setcachestats */
+ NULL, /* hashsize */
+ NULL, /* nodefullname */
+ NULL /* getsize */
+};
+
+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, 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:
+ (void)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 sdb_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,
+ 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 = &sdb_rdataset_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);
+ sdbiter->current = ISC_LIST_NEXT(sdbiter->current, link);
+ }
+ 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..11c4c73
--- /dev/null
+++ b/lib/dns/sdlz.c
@@ -0,0 +1,2172 @@
+/*
+ * Portions Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * 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.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.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;
+ dns_dbversion_t *future_version;
+ int dummy_version;
+};
+
+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 28800U /* 8 hours */
+#define SDLZ_DEFAULT_RETRY 7200U /* 2 hours */
+#define SDLZ_DEFAULT_EXPIRE 604800U /* 7 days */
+#define SDLZ_DEFAULT_MINIMUM 86400U /* 1 day */
+
+/* This is a reasonable value */
+#define SDLZ_DEFAULT_TTL (60 * 60 * 24)
+
+#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.
+ */
+static isc_result_t getnodedata(dns_db_t *db, dns_name_t *name,
+ bool create, unsigned int options,
+ dns_clientinfomethods_t *methods,
+ dns_clientinfo_t *clientinfo,
+ dns_dbnode_t **nodep);
+
+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
+ */
+
+/*
+ * Log a message at the given level
+ */
+static void
+sdlz_log(int level, const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(level),
+ fmt, ap);
+ va_end(ap);
+}
+
+/*% 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;
+
+ (void)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);
+ bool need_destroy = false;
+
+ REQUIRE(VALID_SDLZDB(sdlz));
+ LOCK(&sdlz->refcnt_lock);
+ REQUIRE(sdlz->references > 0);
+ sdlz->references--;
+ if (sdlz->references == 0)
+ need_destroy = true;
+ UNLOCK(&sdlz->refcnt_lock);
+
+ if (need_destroy)
+ destroy(sdlz);
+
+ *dbp = NULL;
+}
+
+static isc_result_t
+beginload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
+ UNUSED(db);
+ UNUSED(callbacks);
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static isc_result_t
+endload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
+ UNUSED(db);
+ UNUSED(callbacks);
+ 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) {
+ dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db;
+ REQUIRE(VALID_SDLZDB(sdlz));
+ REQUIRE(versionp != NULL && *versionp == NULL);
+
+ *versionp = (void *) &sdlz->dummy_version;
+ return;
+}
+
+static isc_result_t
+newversion(dns_db_t *db, dns_dbversion_t **versionp) {
+ dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db;
+ char origin[DNS_NAME_MAXTEXT + 1];
+ isc_result_t result;
+
+ REQUIRE(VALID_SDLZDB(sdlz));
+
+ if (sdlz->dlzimp->methods->newversion == NULL)
+ return (ISC_R_NOTIMPLEMENTED);
+
+ dns_name_format(&sdlz->common.origin, origin, sizeof(origin));
+
+ result = sdlz->dlzimp->methods->newversion(origin,
+ sdlz->dlzimp->driverarg,
+ sdlz->dbdata, versionp);
+ if (result != ISC_R_SUCCESS) {
+ sdlz_log(ISC_LOG_ERROR,
+ "sdlz newversion on origin %s failed : %s",
+ origin, isc_result_totext(result));
+ return (result);
+ }
+
+ sdlz->future_version = *versionp;
+ return (ISC_R_SUCCESS);
+}
+
+static void
+attachversion(dns_db_t *db, dns_dbversion_t *source, dns_dbversion_t **targetp)
+{
+ dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db;
+
+ REQUIRE(VALID_SDLZDB(sdlz));
+ REQUIRE(source != NULL && source == (void *)&sdlz->dummy_version);
+
+ *targetp = source;
+}
+
+static void
+closeversion(dns_db_t *db, dns_dbversion_t **versionp, bool commit) {
+ dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db;
+ char origin[DNS_NAME_MAXTEXT + 1];
+
+ REQUIRE(VALID_SDLZDB(sdlz));
+ REQUIRE(versionp != NULL);
+
+ if (*versionp == (void *)&sdlz->dummy_version) {
+ *versionp = NULL;
+ return;
+ }
+
+ REQUIRE(*versionp == sdlz->future_version);
+ REQUIRE(sdlz->dlzimp->methods->closeversion != NULL);
+
+ dns_name_format(&sdlz->common.origin, origin, sizeof(origin));
+
+ sdlz->dlzimp->methods->closeversion(origin, commit,
+ sdlz->dlzimp->driverarg,
+ sdlz->dbdata, versionp);
+ if (*versionp != NULL)
+ sdlz_log(ISC_LOG_ERROR,
+ "sdlz closeversion on origin %s failed", origin);
+
+ sdlz->future_version = 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
+getnodedata(dns_db_t *db, dns_name_t *name, bool create,
+ unsigned int options, dns_clientinfomethods_t *methods,
+ dns_clientinfo_t *clientinfo, 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];
+ bool isorigin;
+ dns_sdlzauthorityfunc_t authority;
+
+ REQUIRE(VALID_SDLZDB(sdlz));
+ REQUIRE(nodep != NULL && *nodep == NULL);
+
+ if (sdlz->dlzimp->methods->newversion == NULL) {
+ REQUIRE(create == false);
+ }
+
+ 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(&sdlz->common.origin);
+ dns_name_init(&relname, NULL);
+ dns_name_getlabelsequence(name, 0, labels, &relname);
+ result = dns_name_totext(&relname, true, &b);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ } else {
+ result = dns_name_totext(name, 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, 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,
+ methods, clientinfo);
+
+ /*
+ * If the name was not found and DNS_DBFIND_NOWILD is not
+ * set, then we try to find a wildcard entry.
+ *
+ * If DNS_DBFIND_NOZONECUT is set and there are multiple
+ * levels between the host and the zone origin, we also look
+ * for wildcards at each level.
+ */
+ if (result == ISC_R_NOTFOUND && !create &&
+ (options & DNS_DBFIND_NOWILD) == 0)
+ {
+ unsigned int i, dlabels, nlabels;
+
+ nlabels = dns_name_countlabels(name);
+ dlabels = nlabels - dns_name_countlabels(&sdlz->common.origin);
+ for (i = 0; i < dlabels; i++) {
+ char wildstr[DNS_NAME_MAXTEXT + 1];
+ dns_fixedname_t fixed;
+ dns_name_t *wild;
+
+ dns_fixedname_init(&fixed);
+ if (i == dlabels)
+ wild = dns_wildcardname;
+ else {
+ wild = dns_fixedname_name(&fixed);
+ dns_name_getlabelsequence(name, i + 1,
+ dlabels - i - 1,
+ wild);
+ result = dns_name_concatenate(dns_wildcardname,
+ wild, wild, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+
+ isc_buffer_init(&b, wildstr, sizeof(wildstr));
+ result = dns_name_totext(wild, true, &b);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ isc_buffer_putuint8(&b, 0);
+
+ result = sdlz->dlzimp->methods->lookup(zonestr, wildstr,
+ sdlz->dlzimp->driverarg,
+ sdlz->dbdata, node,
+ methods, clientinfo);
+ if (result == ISC_R_SUCCESS)
+ break;
+ }
+ }
+
+ MAYBE_UNLOCK(sdlz->dlzimp);
+
+ if (result == ISC_R_NOTFOUND && (isorigin || create))
+ result = ISC_R_SUCCESS;
+
+ if (result != ISC_R_SUCCESS) {
+ 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);
+ }
+ }
+
+ if (node->name == NULL) {
+ node->name = isc_mem_get(sdlz->common.mctx,
+ sizeof(dns_name_t));
+ if (node->name == NULL) {
+ destroynode(node);
+ return (ISC_R_NOMEMORY);
+ }
+ dns_name_init(node->name, NULL);
+ result = dns_name_dup(name, sdlz->common.mctx, node->name);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(sdlz->common.mctx, node->name,
+ sizeof(dns_name_t));
+ destroynode(node);
+ return (result);
+ }
+ }
+
+ *nodep = node;
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+findnodeext(dns_db_t *db, dns_name_t *name, bool create,
+ dns_clientinfomethods_t *methods, dns_clientinfo_t *clientinfo,
+ dns_dbnode_t **nodep)
+{
+ return (getnodedata(db, name, create, 0, methods, clientinfo, nodep));
+}
+
+static isc_result_t
+findnode(dns_db_t *db, dns_name_t *name, bool create,
+ dns_dbnode_t **nodep)
+{
+ return (getnodedata(db, name, create, 0, NULL, NULL, nodep));
+}
+
+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;
+ bool need_destroy = 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 = 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, 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 = (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
+findext(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_clientinfomethods_t *methods, dns_clientinfo_t *clientinfo,
+ 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*)&sdlz->dummy_version ||
+ version == sdlz->future_version);
+
+ UNUSED(sdlz);
+
+ if (!dns_name_issubdomain(name, &db->origin))
+ return (DNS_R_NXDOMAIN);
+
+ olabels = dns_name_countlabels(&db->origin);
+ nlabels = dns_name_countlabels(name);
+
+ xname = dns_fixedname_initname(&fname);
+
+ if (rdataset == NULL) {
+ dns_rdataset_init(&xrdataset);
+ rdataset = &xrdataset;
+ }
+
+ result = DNS_R_NXDOMAIN;
+
+ /*
+ * If we're not walking down searching for zone
+ * cuts, we can cut straight to the chase
+ */
+ if ((options & DNS_DBFIND_NOZONECUT) != 0) {
+ i = nlabels;
+ goto search;
+ }
+
+ for (i = olabels; i <= nlabels; i++) {
+ search:
+ /*
+ * Look up the next label.
+ */
+ dns_name_getlabelsequence(name, nlabels - i, i, xname);
+ result = getnodedata(db, xname, false, options,
+ methods, clientinfo, &node);
+ if (result == ISC_R_NOTFOUND) {
+ result = DNS_R_NXDOMAIN;
+ continue;
+ } else if (result != ISC_R_SUCCESS)
+ break;
+
+ /*
+ * 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, glue is ok, or there are known to be no zone cuts.
+ */
+ if (i != olabels && (options & DNS_DBFIND_GLUEOK) == 0 &&
+ (options & DNS_DBFIND_NOZONECUT) == 0)
+ {
+ result = findrdataset(db, node, version,
+ dns_rdatatype_ns, 0, now,
+ rdataset, sigrdataset);
+
+ if (result == ISC_R_SUCCESS &&
+ i == nlabels && type == dns_rdatatype_any)
+ {
+ result = DNS_R_ZONECUT;
+ dns_rdataset_disassociate(rdataset);
+ if (sigrdataset != NULL &&
+ dns_rdataset_isassociated(sigrdataset))
+ dns_rdataset_disassociate(sigrdataset);
+ break;
+ } else if (result == ISC_R_SUCCESS) {
+ 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
+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)
+{
+ return (findext(db, name, version, type, options, now, nodep,
+ foundname, NULL, NULL, rdataset, sigrdataset));
+}
+
+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_sdlz_db_t *sdlz = (dns_sdlz_db_t *) db;
+ sdlz_rdatasetiter_t *iterator;
+
+ REQUIRE(VALID_SDLZDB(sdlz));
+
+ REQUIRE(version == NULL ||
+ version == (void*)&sdlz->dummy_version ||
+ version == sdlz->future_version);
+
+ 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
+modrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ dns_rdataset_t *rdataset, unsigned int options,
+ dns_sdlzmodrdataset_t mod_function)
+{
+ dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db;
+ dns_master_style_t *style = NULL;
+ isc_result_t result;
+ isc_buffer_t *buffer = NULL;
+ isc_mem_t *mctx;
+ dns_sdlznode_t *sdlznode;
+ char *rdatastr = NULL;
+ char name[DNS_NAME_MAXTEXT + 1];
+
+ REQUIRE(VALID_SDLZDB(sdlz));
+
+ if (mod_function == NULL)
+ return (ISC_R_NOTIMPLEMENTED);
+
+ sdlznode = (dns_sdlznode_t *)node;
+
+ UNUSED(options);
+
+ dns_name_format(sdlznode->name, name, sizeof(name));
+
+ mctx = sdlz->common.mctx;
+
+ result = isc_buffer_allocate(mctx, &buffer, 1024);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = dns_master_stylecreate(&style, 0, 0, 0, 0, 0, 0, 1, mctx);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = dns_master_rdatasettotext(sdlznode->name, rdataset,
+ style, buffer);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ if (isc_buffer_usedlength(buffer) < 1) {
+ result = ISC_R_BADADDRESSFORM;
+ goto cleanup;
+ }
+
+ rdatastr = isc_buffer_base(buffer);
+ if (rdatastr == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ rdatastr[isc_buffer_usedlength(buffer) - 1] = 0;
+
+ MAYBE_LOCK(sdlz->dlzimp);
+ result = mod_function(name, rdatastr, sdlz->dlzimp->driverarg,
+ sdlz->dbdata, version);
+ MAYBE_UNLOCK(sdlz->dlzimp);
+
+cleanup:
+ isc_buffer_free(&buffer);
+ if (style != NULL)
+ dns_master_styledestroy(&style, mctx);
+
+ return (result);
+}
+
+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_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db;
+ isc_result_t result;
+
+ UNUSED(now);
+ UNUSED(addedrdataset);
+ REQUIRE(VALID_SDLZDB(sdlz));
+
+ if (sdlz->dlzimp->methods->addrdataset == NULL)
+ return (ISC_R_NOTIMPLEMENTED);
+
+ result = modrdataset(db, node, version, rdataset, options,
+ sdlz->dlzimp->methods->addrdataset);
+ 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_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db;
+ isc_result_t result;
+
+ UNUSED(newrdataset);
+ REQUIRE(VALID_SDLZDB(sdlz));
+
+ if (sdlz->dlzimp->methods->subtractrdataset == NULL) {
+ return (ISC_R_NOTIMPLEMENTED);
+ }
+
+ result = modrdataset(db, node, version, rdataset, options,
+ sdlz->dlzimp->methods->subtractrdataset);
+ 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_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db;
+ char name[DNS_NAME_MAXTEXT + 1];
+ char b_type[DNS_RDATATYPE_FORMATSIZE];
+ dns_sdlznode_t *sdlznode;
+ isc_result_t result;
+
+ UNUSED(covers);
+
+ REQUIRE(VALID_SDLZDB(sdlz));
+
+ if (sdlz->dlzimp->methods->delrdataset == NULL)
+ return (ISC_R_NOTIMPLEMENTED);
+
+ sdlznode = (dns_sdlznode_t *)node;
+ dns_name_format(sdlznode->name, name, sizeof(name));
+ dns_rdatatype_format(type, b_type, sizeof(b_type));
+
+ MAYBE_LOCK(sdlz->dlzimp);
+ result = sdlz->dlzimp->methods->delrdataset(name, b_type,
+ sdlz->dlzimp->driverarg,
+ sdlz->dbdata, version);
+ MAYBE_UNLOCK(sdlz->dlzimp);
+
+ return (result);
+}
+
+static bool
+issecure(dns_db_t *db) {
+ UNUSED(db);
+
+ return (false);
+}
+
+static unsigned int
+nodecount(dns_db_t *db) {
+ UNUSED(db);
+
+ return (0);
+}
+
+static bool
+ispersistent(dns_db_t *db) {
+ UNUSED(db);
+ return (true);
+}
+
+static void
+overmem(dns_db_t *db, bool over) {
+ UNUSED(db);
+ UNUSED(over);
+}
+
+static void
+settask(dns_db_t *db, isc_task_t *task) {
+ UNUSED(db);
+ UNUSED(task);
+}
+
+/*
+ * getoriginnode() is used by the update code to find the
+ * dns_rdatatype_dnskey record for a zone
+ */
+static isc_result_t
+getoriginnode(dns_db_t *db, dns_dbnode_t **nodep) {
+ dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db;
+ isc_result_t result;
+
+ REQUIRE(VALID_SDLZDB(sdlz));
+ if (sdlz->dlzimp->methods->newversion == NULL)
+ return (ISC_R_NOTIMPLEMENTED);
+
+ result = getnodedata(db, &sdlz->common.origin, false,
+ 0, NULL, NULL, nodep);
+ if (result != ISC_R_SUCCESS)
+ sdlz_log(ISC_LOG_ERROR, "sdlz getoriginnode failed: %s",
+ isc_result_totext(result));
+ return (result);
+}
+
+static dns_dbmethods_t sdlzdb_methods = {
+ attach,
+ detach,
+ beginload,
+ endload,
+ NULL,
+ dump,
+ currentversion,
+ newversion,
+ attachversion,
+ closeversion,
+ findnode,
+ find,
+ findzonecut,
+ attachnode,
+ detachnode,
+ expirenode,
+ printnode,
+ createiterator,
+ findrdataset,
+ allrdatasets,
+ addrdataset,
+ subtractrdataset,
+ deleterdataset,
+ issecure,
+ nodecount,
+ ispersistent,
+ overmem,
+ settask,
+ getoriginnode,
+ NULL, /* transfernode */
+ NULL, /* getnsec3parameters */
+ NULL, /* findnsec3node */
+ NULL, /* setsigningtime */
+ NULL, /* getsigningtime */
+ NULL, /* resigned */
+ NULL, /* isdnssec */
+ NULL, /* getrrsetstats */
+ NULL, /* rpz_attach */
+ NULL, /* rpz_ready */
+ findnodeext,
+ findext,
+ NULL, /* setcachestats */
+ NULL, /* hashsize */
+ NULL, /* nodefullname */
+ NULL /* getsize */
+};
+
+/*
+ * 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);
+ sdlziter->current = ISC_LIST_NEXT(sdlziter->current, link);
+ }
+ 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,
+ 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, 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 */
+ sdlz_log(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) {
+ sdlz_log(ISC_LOG_DEBUG(2), "SDLZ driver loaded successfully.");
+ } else {
+ sdlz_log(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 */
+ sdlz_log(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_clientinfomethods_t *methods,
+ dns_clientinfo_t *clientinfo,
+ 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, 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,
+ methods, clientinfo);
+ 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 isc_result_t
+dns_sdlzconfigure(void *driverarg, void *dbdata,
+ dns_view_t *view, dns_dlzdb_t *dlzdb)
+{
+ isc_result_t result;
+ dns_sdlzimplementation_t *imp;
+
+ REQUIRE(driverarg != NULL);
+
+ imp = (dns_sdlzimplementation_t *) driverarg;
+
+ /* Call SDLZ driver's configure method */
+ if (imp->methods->configure != NULL) {
+ MAYBE_LOCK(imp);
+ result = imp->methods->configure(view, dlzdb,
+ imp->driverarg, dbdata);
+ MAYBE_UNLOCK(imp);
+ } else {
+ result = ISC_R_SUCCESS;
+ }
+
+ return (result);
+}
+
+static bool
+dns_sdlzssumatch(dns_name_t *signer, dns_name_t *name, isc_netaddr_t *tcpaddr,
+ dns_rdatatype_t type, const dst_key_t *key, void *driverarg,
+ void *dbdata)
+{
+ dns_sdlzimplementation_t *imp;
+ char b_signer[DNS_NAME_FORMATSIZE];
+ char b_name[DNS_NAME_FORMATSIZE];
+ char b_addr[ISC_NETADDR_FORMATSIZE];
+ char b_type[DNS_RDATATYPE_FORMATSIZE];
+ char b_key[DST_KEY_FORMATSIZE];
+ isc_buffer_t *tkey_token = NULL;
+ isc_region_t token_region = { NULL, 0 };
+ uint32_t token_len = 0;
+ bool ret;
+
+ REQUIRE(driverarg != NULL);
+
+ imp = (dns_sdlzimplementation_t *) driverarg;
+ if (imp->methods->ssumatch == NULL)
+ return (false);
+
+ /*
+ * Format the request elements. sdlz operates on strings, not
+ * structures
+ */
+ if (signer != NULL)
+ dns_name_format(signer, b_signer, sizeof(b_signer));
+ else
+ b_signer[0] = 0;
+
+ dns_name_format(name, b_name, sizeof(b_name));
+
+ if (tcpaddr != NULL)
+ isc_netaddr_format(tcpaddr, b_addr, sizeof(b_addr));
+ else
+ b_addr[0] = 0;
+
+ dns_rdatatype_format(type, b_type, sizeof(b_type));
+
+ if (key != NULL) {
+ dst_key_format(key, b_key, sizeof(b_key));
+ tkey_token = dst_key_tkeytoken(key);
+ } else
+ b_key[0] = 0;
+
+ if (tkey_token != NULL) {
+ isc_buffer_region(tkey_token, &token_region);
+ token_len = token_region.length;
+ }
+
+ MAYBE_LOCK(imp);
+ ret = imp->methods->ssumatch(b_signer, b_name, b_addr, b_type, b_key,
+ token_len,
+ token_len != 0 ? token_region.base : NULL,
+ imp->driverarg, dbdata);
+ MAYBE_UNLOCK(imp);
+ return (ret);
+}
+
+static dns_dlzmethods_t sdlzmethods = {
+ dns_sdlzcreate,
+ dns_sdlzdestroy,
+ dns_sdlzfindzone,
+ dns_sdlzallowzonexfr,
+ dns_sdlzconfigure,
+ dns_sdlzssumatch
+};
+
+/*
+ * 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);
+ dns_rdatalist_init(rdatalist);
+ rdatalist->rdclass = lookup->sdlz->common.rdclass;
+ rdatalist->type = typeval;
+ rdatalist->ttl = ttl;
+ ISC_LIST_APPEND(lookup->lists, rdatalist, link);
+ } else
+ if (rdatalist->ttl > ttl) {
+ /*
+ * BIND9 doesn't enforce all RRs in an RRset
+ * having the same TTL, as per RFC 2136,
+ * section 7.12. If a DLZ backend has
+ * different TTLs, then the best
+ * we can do is return the lowest.
+ */
+ rdatalist->ttl = ttl;
+ }
+
+ 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_constinit(&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, false,
+ mctx, rdatabuf,
+ &lookup->callbacks);
+ if (result != ISC_R_SUCCESS)
+ isc_buffer_free(&rdatabuf);
+ if (size >= 65535)
+ break;
+ size *= 2;
+ if (size >= 65535)
+ size = 65535;
+ } 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;
+
+ newname = dns_fixedname_initname(&fnewname);
+
+ if ((sdlz->dlzimp->flags & DNS_SDLZFLAG_RELATIVERDATA) != 0)
+ origin = &sdlz->common.origin;
+ else
+ origin = dns_rootname;
+ isc_buffer_constinit(&b, name, strlen(name));
+ isc_buffer_add(&b, strlen(name));
+
+ result = dns_name_fromtext(newname, &b, origin, 0, 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,
+ 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 */
+ sdlz_log(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 */
+ sdlz_log(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;
+}
+
+
+isc_result_t
+dns_sdlz_setdb(dns_dlzdb_t *dlzdatabase, dns_rdataclass_t rdclass,
+ dns_name_t *name, dns_db_t **dbp)
+{
+ isc_result_t result;
+
+ result = dns_sdlzcreateDBP(dlzdatabase->mctx,
+ dlzdatabase->implementation->driverarg,
+ dlzdatabase->dbdata, name, rdclass, dbp);
+ return (result);
+}
diff --git a/lib/dns/soa.c b/lib/dns/soa.c
new file mode 100644
index 0000000..9be3467
--- /dev/null
+++ b/lib/dns/soa.c
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <string.h>
+
+#include <isc/buffer.h>
+#include <isc/util.h>
+
+#include <dns/rdata.h>
+#include <dns/rdatastruct.h>
+#include <dns/soa.h>
+
+static inline 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(uint32_t val, unsigned char *p) {
+ p[0] = (uint8_t)(val >> 24);
+ p[1] = (uint8_t)(val >> 16);
+ p[2] = (uint8_t)(val >> 8);
+ p[3] = (uint8_t)(val >> 0);
+}
+
+static 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_result_t
+dns_soa_buildrdata(dns_name_t *origin, dns_name_t *contact,
+ dns_rdataclass_t rdclass,
+ uint32_t serial, uint32_t refresh,
+ uint32_t retry, uint32_t expire,
+ uint32_t minimum, unsigned char *buffer,
+ dns_rdata_t *rdata) {
+ dns_rdata_soa_t soa;
+ isc_buffer_t rdatabuf;
+
+ REQUIRE(origin != NULL);
+ REQUIRE(contact != NULL);
+
+ memset(buffer, 0, DNS_SOA_BUFFERSIZE);
+ isc_buffer_init(&rdatabuf, buffer, DNS_SOA_BUFFERSIZE);
+
+ soa.common.rdtype = dns_rdatatype_soa;
+ soa.common.rdclass = rdclass;
+ soa.mctx = NULL;
+ soa.serial = serial;
+ soa.refresh = refresh;
+ soa.retry = retry;
+ soa.expire = expire;
+ soa.minimum = minimum;
+ dns_name_init(&soa.origin, NULL);
+ dns_name_clone(origin, &soa.origin);
+ dns_name_init(&soa.contact, NULL);
+ dns_name_clone(contact, &soa.contact);
+
+ return (dns_rdata_fromstruct(rdata, rdclass, dns_rdatatype_soa,
+ &soa, &rdatabuf));
+}
+
+uint32_t
+dns_soa_getserial(dns_rdata_t *rdata) {
+ return soa_get(rdata, 0);
+}
+uint32_t
+dns_soa_getrefresh(dns_rdata_t *rdata) {
+ return soa_get(rdata, 4);
+}
+uint32_t
+dns_soa_getretry(dns_rdata_t *rdata) {
+ return soa_get(rdata, 8);
+}
+uint32_t
+dns_soa_getexpire(dns_rdata_t *rdata) {
+ return soa_get(rdata, 12);
+}
+uint32_t
+dns_soa_getminimum(dns_rdata_t *rdata) {
+ return soa_get(rdata, 16);
+}
+
+static void
+soa_set(dns_rdata_t *rdata, 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(uint32_t val, dns_rdata_t *rdata) {
+ soa_set(rdata, val, 0);
+}
+void
+dns_soa_setrefresh(uint32_t val, dns_rdata_t *rdata) {
+ soa_set(rdata, val, 4);
+}
+void
+dns_soa_setretry(uint32_t val, dns_rdata_t *rdata) {
+ soa_set(rdata, val, 8);
+}
+void
+dns_soa_setexpire(uint32_t val, dns_rdata_t *rdata) {
+ soa_set(rdata, val, 12);
+}
+void
+dns_soa_setminimum(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..abf9b76
--- /dev/null
+++ b/lib/dns/spnego.asn1
@@ -0,0 +1,50 @@
+-- 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.)
+
+-- 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..ad77f24
--- /dev/null
+++ b/lib/dns/spnego.c
@@ -0,0 +1,1825 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \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 <inttypes.h>
+#include <stdbool.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/safe.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 */
+
+#ifndef ERROR_TABLE_BASE_asn1
+/* these may be brought in already via gssapi_krb5.h */
+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
+#endif
+
+#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 * xclass, Der_type * type,
+ int *tag, size_t * size);
+
+static int
+der_match_tag(const unsigned char *p, size_t len,
+ Der_class xclass, Der_type type,
+ int tag, size_t * size);
+static int
+der_match_tag_and_length(const unsigned char *p, size_t len,
+ Der_class xclass, 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, void *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 xclass, 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 void *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"
+
+/*
+ * Force the oid arrays to be uint64_t aligned to silence warnings
+ * about the arrays not being properly aligned for (void *).
+ */
+typedef union { unsigned char b[8]; uint64_t _align; } aligned8;
+typedef union { unsigned char b[16]; uint64_t _align[2]; } aligned16;
+
+static aligned16 gss_krb5_mech_oid_bytes = {
+ { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x01, 0x02, 0x02 }
+};
+
+static gss_OID_desc gss_krb5_mech_oid_desc = {
+ 9, gss_krb5_mech_oid_bytes.b
+};
+
+static gss_OID GSS_KRB5_MECH = &gss_krb5_mech_oid_desc;
+
+static aligned16 gss_mskrb5_mech_oid_bytes = {
+ { 0x2a, 0x86, 0x48, 0x82, 0xf7, 0x12, 0x01, 0x02, 0x02 }
+};
+
+static gss_OID_desc gss_mskrb5_mech_oid_desc = {
+ 9, gss_mskrb5_mech_oid_bytes.b
+};
+
+static gss_OID GSS_MSKRB5_MECH = &gss_mskrb5_mech_oid_desc;
+
+static aligned8 gss_spnego_mech_oid_bytes = {
+ { 0x2b, 0x06, 0x01, 0x05, 0x05, 0x02 }
+};
+
+static gss_OID_desc gss_spnego_mech_oid_desc = {
+ 6, gss_spnego_mech_oid_bytes.b
+};
+
+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 gssoid)
+{
+ unsigned char *p;
+ size_t len;
+
+ if (token->length == 0U)
+ return (GSS_S_DEFECTIVE_TOKEN);
+
+ p = token->value;
+ if (*p++ != 0x60)
+ return (GSS_S_DEFECTIVE_TOKEN);
+ len = *p++;
+ if (len & 0x80) {
+ if ((len & 0x7f) > 4U)
+ return (GSS_S_DEFECTIVE_TOKEN);
+ p += len & 0x7f;
+ }
+ if (*p++ != 0x06)
+ return (GSS_S_DEFECTIVE_TOKEN);
+
+ if (((OM_uint32) *p++) != gssoid->length)
+ return (GSS_S_DEFECTIVE_TOKEN);
+
+ return (isc_safe_memcompare(p, gssoid->elements, gssoid->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 = 0;
+
+ 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);
+ }
+ memmove(*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 != 0U) {
+ 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 = GSS_S_COMPLETE;
+ 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) {
+ unsigned 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) {
+ free_NegTokenInit(&init_token);
+ return (GSS_S_DEFECTIVE_TOKEN);
+ }
+ if (mech_len == GSS_KRB5_MECH->length &&
+ isc_safe_memequal(GSS_KRB5_MECH->elements,
+ mechbuf + sizeof(mechbuf) - mech_len,
+ mech_len))
+ {
+ found = 1;
+ break;
+ }
+ if (mech_len == GSS_MSKRB5_MECH->length &&
+ isc_safe_memequal(GSS_MSKRB5_MECH->elements,
+ mechbuf + sizeof(mechbuf) - mech_len,
+ mech_len))
+ {
+ found = 1;
+ if (i == 0)
+ pref = GSS_MSKRB5_MECH;
+ break;
+ }
+ }
+
+ if (!found) {
+ free_NegTokenInit(&init_token);
+ 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)) {
+ free_NegTokenInit(&init_token);
+ send_reject(&minor_status2, output_token);
+ return (major_status);
+ }
+ ot = &obuf;
+ }
+ ret = send_accept(&minor_status2, output_token, ot, pref);
+ free_NegTokenInit(&init_token);
+ if (ot != NULL && ot->length != 0U)
+ gss_release_buffer(&minor_status2, ot);
+
+ return (ret != GSS_S_COMPLETE ? (OM_uint32) ret : major_status);
+}
+
+/* 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 < 1U)
+ 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 (!isc_safe_memequal(p, mech->elements, mech->length))
+ 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 > 0U) {
+ 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 <= 0U)
+ return (ASN1_OVERRUN);
+ --len;
+ v = *p++;
+ if (v < 128U) {
+ *val = v;
+ if (size)
+ *size = 1;
+ } else {
+ int e;
+ size_t l;
+ unsigned tmp;
+
+ if (v == 0x80U) {
+ *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;
+ if (len != 0U) {
+ data->data = malloc(len);
+ if (data->data == NULL)
+ return (ENOMEM);
+ memmove(data->data, p, len);
+ } else
+ data->data = NULL;
+ 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;
+
+ data->components = NULL;
+ data->length = 0;
+ if (len < 1U) {
+ return (ASN1_OVERRUN);
+ }
+
+ data->components = malloc(len * sizeof(*data->components));
+ if (data->components == NULL) {
+ return (ENOMEM);
+ }
+ data->components[0] = (*p) / 40;
+ data->components[1] = (*p) % 40;
+ --len;
+ ++p;
+ for (n = 2; len > 0U; ++n) {
+ unsigned u = 0;
+
+ do {
+ --len;
+ u = u * 128 + (*p++ % 128);
+ } while (len > 0U && 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 *xclass, Der_type *type,
+ int *tag, size_t *size)
+{
+ if (len < 1U)
+ return (ASN1_OVERRUN);
+ *xclass = (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 xclass, 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 (xclass != 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 xclass, 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, xclass, 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;
+ POST(len);
+ ret += l;
+ if (size)
+ *size = ret;
+ return (0);
+}
+
+static int
+decode_enumerated(const unsigned char *p, size_t len, void *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;
+ POST(p); POST(len);
+ 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;
+
+ k->data = NULL;
+ k->length = 0;
+
+ 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;
+ POST(p); POST(len);
+ 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;
+ POST(p); POST(len);
+ 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 < 128U)
+ return (1);
+ else
+ return (len_unsigned((unsigned int)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 > 0U && val) {
+ *p-- = val % 256;
+ val /= 256;
+ --len;
+ }
+ if (val != 0)
+ return (ASN1_OVERFLOW);
+ else {
+ *size = base - p;
+ return (0);
+ }
+ } else if (len < 1U)
+ 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 < 1U)
+ return (ASN1_OVERFLOW);
+ *p-- = val % 256;
+ len--;
+ val /= 256;
+ } while (val);
+ if (p[1] >= 128) {
+ if (len < 1U)
+ return (ASN1_OVERFLOW);
+ *p-- = 0;
+ len--;
+ POST(len);
+ }
+ } else {
+ val = ~val;
+ do {
+ if (len < 1U)
+ return (ASN1_OVERFLOW);
+ *p-- = ~(val % 256);
+ len--;
+ val /= 256;
+ } while (val);
+ if (p[1] < 128) {
+ if (len < 1U)
+ return (ASN1_OVERFLOW);
+ *p-- = 0xff;
+ len--;
+ POST(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 < 1U)
+ return (ASN1_OVERFLOW);
+ if (val < 128U) {
+ *p = (unsigned char)val;
+ *size = 1;
+ return (0);
+ } else {
+ size_t l;
+ int e;
+
+ e = der_put_unsigned(p, len - 1, (unsigned int)val, &l);
+ if (e)
+ return (e);
+ p -= l;
+ *p = 0x80 | (unsigned char)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;
+ POST(len);
+ memmove(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;
+ size_t n;
+
+ for (n = data->length; n >= 3u; --n) {
+ unsigned u = data->components[n - 1];
+
+ if (len < 1U)
+ return (ASN1_OVERFLOW);
+ *p-- = u % 128;
+ u /= 128;
+ --len;
+ while (u > 0) {
+ if (len < 1U)
+ return (ASN1_OVERFLOW);
+ *p-- = 128 + u % 128;
+ u /= 128;
+ --len;
+ }
+ }
+ if (len < 1U)
+ 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 xclass, Der_type type,
+ int tag, size_t *size)
+{
+ if (len < 1U)
+ return (ASN1_OVERFLOW);
+ *p = (xclass << 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 xclass, 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, xclass, type, tag, &l);
+ if (e)
+ return (e);
+ p -= l;
+ len -= l;
+ POST(p); POST(len);
+ ret += l;
+ *size = ret;
+ return (0);
+}
+
+static int
+encode_enumerated(unsigned char *p, size_t len, const void *data, size_t *size)
+{
+ unsigned num = *(const unsigned *)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;
+ POST(p); POST(len);
+ 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;
+ POST(p); POST(len);
+ 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;
+ POST(p); POST(len);
+ 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;
+ memmove(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 != 0U)
+ gss_release_buffer(minor_status, output_token);
+ return (GSS_S_FAILURE);
+ }
+ memmove(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 < 1U)
+ 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 > 0U) {
+ 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);
+ if (buf == NULL) {
+ *minor_status = ENOMEM;
+ ret = GSS_S_FAILURE;
+ goto end;
+ }
+
+ 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 != 0U)
+ 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 &&
+ isc_safe_memequal(GSS_KRB5_MECH->elements, p, mech_len))
+ 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 &&
+ isc_safe_memequal(GSS_SPNEGO_MECH->elements, p, mech_len)) {
+ 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) {
+ free_NegTokenResp(&resp);
+ *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 ||
+ !isc_safe_memequal(oidbuf + sizeof(oidbuf) - oidlen,
+ GSS_KRB5_MECH->elements, oidlen))
+ {
+ 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
+ * successfully, 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 == 0U)
+ 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..3118720
--- /dev/null
+++ b/lib/dns/spnego.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \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..6fbce55
--- /dev/null
+++ b/lib/dns/spnego_asn1.c
@@ -0,0 +1,862 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \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; POST(p); POST(len); POST(ret)
+
+static int
+encode_MechType(unsigned char *p, size_t len, const MechType * data, size_t * size)
+{
+ size_t ret = 0;
+ size_t l;
+ int e;
+
+ e = encode_oid(p, len, data, &l);
+ BACK;
+ *size = ret;
+ return 0;
+}
+
+#define FORW if(e) goto fail; p += l; len -= l; ret += l; POST(p); POST(len); POST(ret)
+
+static int
+decode_MechType(const unsigned char *p, size_t len, MechType * data, size_t * size)
+{
+ size_t ret = 0;
+ size_t l;
+ int e;
+
+ memset(data, 0, sizeof(*data));
+ 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 */
+
+
+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;
+
+ for (i = (data)->len - 1; i >= 0; --i) {
+ size_t 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;
+}
+
+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;
+ size_t oldret = ret;
+ ret = 0;
+ (data)->len = 0;
+ (data)->val = NULL;
+ while (ret < origlen) {
+ void *old = (data)->val;
+ (data)->len++;
+ (data)->val = realloc((data)->val, sizeof(*((data)->val)) * (data)->len);
+ if ((data)->val == NULL) {
+ (data)->val = old;
+ (data)->len--;
+ return ENOMEM;
+ }
+ 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 */
+
+
+static int
+encode_ContextFlags(unsigned char *p, size_t len, const ContextFlags * data, size_t * size)
+{
+ size_t ret = 0;
+ size_t l;
+ int e;
+
+ {
+ 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;
+}
+
+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--;
+ POST(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;
+ 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 */
+
+
+static int
+encode_NegTokenInit(unsigned char *p, size_t len, const NegTokenInit * data, size_t * size)
+{
+ size_t ret = 0;
+ size_t l;
+ int e;
+
+ if ((data)->mechListMIC) {
+ size_t 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) {
+ size_t 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) {
+ size_t 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;
+ } {
+ size_t 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;
+}
+
+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 mydce_fix;
+ oldlen = len;
+ if ((mydce_fix = fix_dce(newlen, &len)) < 0)
+ return ASN1_BAD_FORMAT;
+ e = decode_MechTypeList(p, len, &(data)->mechTypes, &l);
+ FORW;
+ if (mydce_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 mydce_fix;
+ oldlen = len;
+ if ((mydce_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 (mydce_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 mydce_fix;
+ oldlen = len;
+ if ((mydce_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 (mydce_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 mydce_fix;
+ oldlen = len;
+ if ((mydce_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 (mydce_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 */
+
+
+static int
+encode_NegTokenResp(unsigned char *p, size_t len, const NegTokenResp * data, size_t * size)
+{
+ size_t ret = 0;
+ size_t l;
+ int e;
+
+ if ((data)->mechListMIC) {
+ size_t 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) {
+ size_t 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) {
+ size_t 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) {
+ size_t 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;
+}
+
+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 mydce_fix;
+ oldlen = len;
+ if ((mydce_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 (mydce_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 mydce_fix;
+ oldlen = len;
+ if ((mydce_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 (mydce_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 mydce_fix;
+ oldlen = len;
+ if ((mydce_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 (mydce_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 mydce_fix;
+ oldlen = len;
+ if ((mydce_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 (mydce_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..66f7b73
--- /dev/null
+++ b/lib/dns/spnego_asn1.pl
@@ -0,0 +1,193 @@
+#!/bin/bin/perl -w
+#
+# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# See the COPYRIGHT file distributed with this work for additional
+# information regarding copyright ownership.
+
+# 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..71ded66
--- /dev/null
+++ b/lib/dns/ssu.c
@@ -0,0 +1,711 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdbool.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/dlz.h>
+#include <dns/fixedname.h>
+#include <dns/name.h>
+#include <dns/ssu.h>
+
+#include <dst/gssapi.h>
+#include <dst/dst.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;
+ bool 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;
+ dns_dlzdb_t *dlzdatabase;
+ 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 = NULL;
+ isc_mem_attach(mctx, &table->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_putanddetach(&table->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;
+ bool done = false;
+
+ REQUIRE(tablep != NULL);
+ table = *tablep;
+ REQUIRE(VALID_SSUTABLE(table));
+
+ LOCK(&table->lock);
+
+ INSIST(table->references > 0);
+ if (--table->references == 0)
+ done = true;
+ UNLOCK(&table->lock);
+
+ *tablep = NULL;
+
+ if (done)
+ destroy(table);
+}
+
+isc_result_t
+dns_ssutable_addrule(dns_ssutable_t *table, bool 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;
+ }
+ memmove(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 bool
+isusertype(dns_rdatatype_t type) {
+ return (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);
+}
+
+bool
+dns_ssutable_checkrules(dns_ssutable_t *table, dns_name_t *signer,
+ dns_name_t *name, isc_netaddr_t *addr,
+ dns_rdatatype_t type, const dst_key_t *key)
+{
+ return (dns_ssutable_checkrules2
+ (table, signer, name, addr,
+ addr == NULL ? false : true,
+ NULL, type, key));
+}
+
+bool
+dns_ssutable_checkrules2(dns_ssutable_t *table, dns_name_t *signer,
+ dns_name_t *name, isc_netaddr_t *addr,
+ bool tcp, const dns_aclenv_t *env,
+ dns_rdatatype_t type, const dst_key_t *key)
+{
+ 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;
+ int match;
+
+ REQUIRE(VALID_SSUTABLE(table));
+ REQUIRE(signer == NULL || dns_name_isabsolute(signer));
+ REQUIRE(dns_name_isabsolute(name));
+ REQUIRE(addr == NULL || env != NULL);
+
+ if (signer == NULL && addr == NULL)
+ return (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_LOCAL:
+ 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_selfsubkrb5:
+ case dns_ssumatchtype_selfsubms:
+ case dns_ssumatchtype_subdomainkrb5:
+ case dns_ssumatchtype_subdomainms:
+ if (signer == NULL)
+ continue;
+ break;
+ case DNS_SSUMATCHTYPE_TCPSELF:
+ case DNS_SSUMATCHTYPE_6TO4SELF:
+ if (!tcp || addr == 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_LOCAL:
+ if (addr == NULL) {
+ continue;
+ }
+ if (!dns_name_issubdomain(name, rule->name)) {
+ continue;
+ }
+ dns_acl_match(addr, NULL, env->localhost,
+ NULL, &match, NULL);
+ if (match == 0) {
+ if (signer != NULL) {
+ isc_log_write(dns_lctx,
+ DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_SSU,
+ ISC_LOG_WARNING,
+ "update-policy local: "
+ "match on session "
+ "key not from "
+ "localhost");
+ }
+ 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:
+ wildcard = dns_fixedname_initname(&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,
+ false))
+ {
+ break;
+ }
+ continue;
+ case dns_ssumatchtype_selfms:
+ if (dst_gssapi_identitymatchesrealmms(signer, name,
+ rule->identity,
+ false))
+ {
+ break;
+ }
+ continue;
+ case dns_ssumatchtype_selfsubkrb5:
+ if (dst_gssapi_identitymatchesrealmkrb5(signer, name,
+ rule->identity,
+ true))
+ {
+ break;
+ }
+ continue;
+ case dns_ssumatchtype_selfsubms:
+ if (dst_gssapi_identitymatchesrealmms(signer, name,
+ rule->identity,
+ true))
+ break;
+ continue;
+ case dns_ssumatchtype_subdomainkrb5:
+ if (!dns_name_issubdomain(name, rule->name))
+ continue;
+ if (dst_gssapi_identitymatchesrealmkrb5(signer, NULL,
+ rule->identity,
+ false))
+ {
+ break;
+ }
+ continue;
+ case dns_ssumatchtype_subdomainms:
+ if (!dns_name_issubdomain(name, rule->name))
+ continue;
+ if (dst_gssapi_identitymatchesrealmms(signer, NULL,
+ rule->identity,
+ false))
+ {
+ break;
+ }
+ continue;
+ case DNS_SSUMATCHTYPE_TCPSELF:
+ tcpself = dns_fixedname_initname(&fixed);
+ reverse_from_address(tcpself, addr);
+ 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:
+ stfself = dns_fixedname_initname(&fixed);
+ stf_from_address(stfself, addr);
+ 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;
+ case DNS_SSUMATCHTYPE_EXTERNAL:
+ if (!dns_ssu_external_match(rule->identity, signer,
+ name, addr, type, key,
+ table->mctx))
+ continue;
+ break;
+ case DNS_SSUMATCHTYPE_DLZ:
+ if (!dns_dlz_ssumatch(table->dlzdatabase, signer,
+ name, addr, type, key))
+ continue;
+ break;
+ }
+
+ if (rule->ntypes == 0) {
+ /*
+ * If this is a DLZ rule, then the DLZ ssu
+ * checks will have already checked
+ * the type.
+ */
+ if (rule->matchtype != DNS_SSUMATCHTYPE_DLZ &&
+ !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 (false);
+}
+
+bool
+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);
+}
+
+/*
+ * Create a specialised SSU table that points at an external DLZ database
+ */
+isc_result_t
+dns_ssutable_createdlz(isc_mem_t *mctx, dns_ssutable_t **tablep,
+ dns_dlzdb_t *dlzdatabase)
+{
+ isc_result_t result;
+ dns_ssurule_t *rule;
+ dns_ssutable_t *table = NULL;
+
+ REQUIRE(tablep != NULL && *tablep == NULL);
+
+ result = dns_ssutable_create(mctx, &table);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ table->dlzdatabase = dlzdatabase;
+
+ rule = isc_mem_get(table->mctx, sizeof(dns_ssurule_t));
+ if (rule == NULL) {
+ dns_ssutable_detach(&table);
+ return (ISC_R_NOMEMORY);
+ }
+
+ rule->identity = NULL;
+ rule->name = NULL;
+ rule->types = NULL;
+ rule->grant = true;
+ rule->matchtype = DNS_SSUMATCHTYPE_DLZ;
+ rule->ntypes = 0;
+ rule->types = NULL;
+ rule->magic = SSURULEMAGIC;
+
+ ISC_LIST_INITANDAPPEND(table->rules, rule, link);
+ *tablep = table;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_ssu_mtypefromstring(const char *str, dns_ssumatchtype_t *mtype) {
+
+ REQUIRE(str != NULL);
+ REQUIRE(mtype != NULL);
+
+ if (strcasecmp(str, "name") == 0) {
+ *mtype = dns_ssumatchtype_name;
+ } else if (strcasecmp(str, "subdomain") == 0) {
+ *mtype = dns_ssumatchtype_subdomain;
+ } else if (strcasecmp(str, "wildcard") == 0) {
+ *mtype = dns_ssumatchtype_wildcard;
+ } else if (strcasecmp(str, "self") == 0) {
+ *mtype = dns_ssumatchtype_self;
+ } else if (strcasecmp(str, "selfsub") == 0) {
+ *mtype = dns_ssumatchtype_selfsub;
+ } else if (strcasecmp(str, "selfwild") == 0) {
+ *mtype = dns_ssumatchtype_selfwild;
+ } else if (strcasecmp(str, "ms-self") == 0) {
+ *mtype = dns_ssumatchtype_selfms;
+ } else if (strcasecmp(str, "ms-selfsub") == 0) {
+ *mtype = dns_ssumatchtype_selfsubms;
+ } else if (strcasecmp(str, "krb5-self") == 0) {
+ *mtype = dns_ssumatchtype_selfkrb5;
+ } else if (strcasecmp(str, "krb5-selfsub") == 0) {
+ *mtype = dns_ssumatchtype_selfsubkrb5;
+ } else if (strcasecmp(str, "ms-subdomain") == 0) {
+ *mtype = dns_ssumatchtype_subdomainms;
+ } else if (strcasecmp(str, "krb5-subdomain") == 0) {
+ *mtype = dns_ssumatchtype_subdomainkrb5;
+ } else if (strcasecmp(str, "tcp-self") == 0) {
+ *mtype = dns_ssumatchtype_tcpself;
+ } else if (strcasecmp(str, "6to4-self") == 0) {
+ *mtype = dns_ssumatchtype_6to4self;
+ } else if (strcasecmp(str, "zonesub") == 0) {
+ *mtype = dns_ssumatchtype_subdomain;
+ } else if (strcasecmp(str, "external") == 0) {
+ *mtype = dns_ssumatchtype_external;
+ } else {
+ return (ISC_R_NOTFOUND);
+ }
+ return (ISC_R_SUCCESS);
+}
diff --git a/lib/dns/ssu_external.c b/lib/dns/ssu_external.c
new file mode 100644
index 0000000..a1b638e
--- /dev/null
+++ b/lib/dns/ssu_external.c
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*
+ * This implements external update-policy rules. This allows permission
+ * to update a zone to be checked by consulting an external daemon (e.g.,
+ * kerberos).
+ */
+
+#include <config.h>
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <unistd.h>
+
+#ifdef ISC_PLATFORM_HAVESYSUNH
+#include <sys/socket.h>
+#include <sys/un.h>
+#endif
+
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/netaddr.h>
+#include <isc/print.h>
+#include <isc/result.h>
+#include <isc/string.h>
+#include <isc/util.h>
+#include <isc/strerror.h>
+
+#include <dns/fixedname.h>
+#include <dns/name.h>
+#include <dns/ssu.h>
+#include <dns/log.h>
+#include <dns/rdatatype.h>
+
+#include <dst/dst.h>
+
+
+static void
+ssu_e_log(int level, const char *fmt, ...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_SECURITY,
+ DNS_LOGMODULE_ZONE, ISC_LOG_DEBUG(level), fmt, ap);
+ va_end(ap);
+}
+
+
+/*
+ * Connect to a UNIX domain socket.
+ */
+static int
+ux_socket_connect(const char *path) {
+ int fd = -1;
+#ifdef ISC_PLATFORM_HAVESYSUNH
+ struct sockaddr_un addr;
+
+ REQUIRE(path != NULL);
+
+ if (strlen(path) > sizeof(addr.sun_path)) {
+ ssu_e_log(3, "ssu_external: socket path '%s' "
+ "longer than system maximum %u",
+ path, sizeof(addr.sun_path));
+ return (-1);
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ strlcpy(addr.sun_path, path, sizeof(addr.sun_path));
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd == -1) {
+ char strbuf[ISC_STRERRORSIZE];
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ ssu_e_log(3, "ssu_external: unable to create socket - %s",
+ strbuf);
+ return (-1);
+ }
+
+ if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+ char strbuf[ISC_STRERRORSIZE];
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ ssu_e_log(3, "ssu_external: unable to connect to "
+ "socket '%s' - %s",
+ path, strbuf);
+ close(fd);
+ return (-1);
+ }
+#endif
+ return (fd);
+}
+
+/* Change this version if you update the format of the request */
+#define SSU_EXTERNAL_VERSION 1
+
+/*
+ * Perform an update-policy rule check against an external application
+ * over a socket.
+ *
+ * This currently only supports local: for unix domain datagram sockets.
+ *
+ * Note that by using a datagram socket and creating a new socket each
+ * time we avoid the need for locking and allow for parallel access to
+ * the authorization server.
+ */
+bool
+dns_ssu_external_match(dns_name_t *identity,
+ dns_name_t *signer, dns_name_t *name,
+ isc_netaddr_t *tcpaddr, dns_rdatatype_t type,
+ const dst_key_t *key, isc_mem_t *mctx)
+{
+ char b_identity[DNS_NAME_FORMATSIZE];
+ char b_signer[DNS_NAME_FORMATSIZE];
+ char b_name[DNS_NAME_FORMATSIZE];
+ char b_addr[ISC_NETADDR_FORMATSIZE];
+ char b_type[DNS_RDATATYPE_FORMATSIZE];
+ char b_key[DST_KEY_FORMATSIZE];
+ isc_buffer_t *tkey_token = NULL;
+ int fd;
+ const char *sock_path;
+ unsigned int req_len;
+ isc_region_t token_region = {NULL, 0};
+ unsigned char *data;
+ isc_buffer_t buf;
+ uint32_t token_len = 0;
+ uint32_t reply;
+ ssize_t ret;
+
+ /* The identity contains local:/path/to/socket */
+ dns_name_format(identity, b_identity, sizeof(b_identity));
+
+ /* For now only local: is supported */
+ if (strncmp(b_identity, "local:", 6) != 0) {
+ ssu_e_log(3, "ssu_external: invalid socket path '%s'",
+ b_identity);
+ return (false);
+ }
+ sock_path = &b_identity[6];
+
+ fd = ux_socket_connect(sock_path);
+ if (fd == -1)
+ return (false);
+
+ if (key != NULL) {
+ dst_key_format(key, b_key, sizeof(b_key));
+ tkey_token = dst_key_tkeytoken(key);
+ } else
+ b_key[0] = 0;
+
+ if (tkey_token != NULL) {
+ isc_buffer_region(tkey_token, &token_region);
+ token_len = token_region.length;
+ }
+
+ /* Format the request elements */
+ if (signer != NULL)
+ dns_name_format(signer, b_signer, sizeof(b_signer));
+ else
+ b_signer[0] = 0;
+
+ dns_name_format(name, b_name, sizeof(b_name));
+
+ if (tcpaddr != NULL)
+ isc_netaddr_format(tcpaddr, b_addr, sizeof(b_addr));
+ else
+ b_addr[0] = 0;
+
+ dns_rdatatype_format(type, b_type, sizeof(b_type));
+
+ /* Work out how big the request will be */
+ req_len = sizeof(uint32_t) + /* Format version */
+ sizeof(uint32_t) + /* Length */
+ strlen(b_signer) + 1 + /* Signer */
+ strlen(b_name) + 1 + /* Name */
+ strlen(b_addr) + 1 + /* Address */
+ strlen(b_type) + 1 + /* Type */
+ strlen(b_key) + 1 + /* Key */
+ sizeof(uint32_t) + /* tkey_token length */
+ token_len; /* tkey_token */
+
+
+ /* format the buffer */
+ data = isc_mem_allocate(mctx, req_len);
+ if (data == NULL) {
+ close(fd);
+ return (false);
+ }
+
+ isc_buffer_init(&buf, data, req_len);
+ isc_buffer_putuint32(&buf, SSU_EXTERNAL_VERSION);
+ isc_buffer_putuint32(&buf, req_len);
+
+ /* Strings must be null-terminated */
+ isc_buffer_putstr(&buf, b_signer);
+ isc_buffer_putuint8(&buf, 0);
+ isc_buffer_putstr(&buf, b_name);
+ isc_buffer_putuint8(&buf, 0);
+ isc_buffer_putstr(&buf, b_addr);
+ isc_buffer_putuint8(&buf, 0);
+ isc_buffer_putstr(&buf, b_type);
+ isc_buffer_putuint8(&buf, 0);
+ isc_buffer_putstr(&buf, b_key);
+ isc_buffer_putuint8(&buf, 0);
+
+ isc_buffer_putuint32(&buf, token_len);
+ if (tkey_token && token_len != 0)
+ isc_buffer_putmem(&buf, token_region.base, token_len);
+
+ ENSURE(isc_buffer_availablelength(&buf) == 0);
+
+ /* Send the request */
+ ret = write(fd, data, req_len);
+ isc_mem_free(mctx, data);
+ if (ret != (ssize_t) req_len) {
+ char strbuf[ISC_STRERRORSIZE];
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ ssu_e_log(3, "ssu_external: unable to send request - %s",
+ strbuf);
+ close(fd);
+ return (false);
+ }
+
+ /* Receive the reply */
+ ret = read(fd, &reply, sizeof(uint32_t));
+ if (ret != (ssize_t) sizeof(uint32_t)) {
+ char strbuf[ISC_STRERRORSIZE];
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ ssu_e_log(3, "ssu_external: unable to receive reply - %s",
+ strbuf);
+ close(fd);
+ return (false);
+ }
+
+ close(fd);
+
+ reply = ntohl(reply);
+
+ if (reply == 0) {
+ ssu_e_log(3, "ssu_external: denied external auth for '%s'",
+ b_name);
+ return (false);
+ } else if (reply == 1) {
+ ssu_e_log(3, "ssu_external: allowed external auth for '%s'",
+ b_name);
+ return (true);
+ }
+
+ ssu_e_log(3, "ssu_external: invalid reply 0x%08x", reply);
+
+ return (false);
+}
diff --git a/lib/dns/stats.c b/lib/dns/stats.c
new file mode 100644
index 0000000..617ed3b
--- /dev/null
+++ b/lib/dns/stats.c
@@ -0,0 +1,475 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/stats.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_rcode = 4
+} 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.
+ */
+/*
+ * types, !types, nxdomain, stale types, stale !types, stale nxdomain
+ */
+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,
+ /* stale counters offset */
+ rdtypecounter_stale = rdtypecounter_nxdomain + 1,
+ rdatasettypecounter_max = rdtypecounter_stale * 2
+};
+
+struct dns_stats {
+ /*% Unlocked */
+ unsigned int magic;
+ dns_statstype_t type;
+ isc_mem_t *mctx;
+ isc_mutex_t lock;
+ isc_stats_t *counters;
+
+ /*% Locked by lock */
+ unsigned int references;
+};
+
+typedef struct rdatadumparg {
+ dns_rdatatypestats_dumper_t fn;
+ void *arg;
+} rdatadumparg_t;
+
+typedef struct opcodedumparg {
+ dns_opcodestats_dumper_t fn;
+ void *arg;
+} opcodedumparg_t;
+
+typedef struct rcodedumparg {
+ dns_rcodestats_dumper_t fn;
+ void *arg;
+} rcodedumparg_t;
+
+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_stats_detach(&stats->counters);
+ DESTROYLOCK(&stats->lock);
+ isc_mem_putanddetach(&stats->mctx, stats, sizeof(*stats));
+ }
+}
+
+/*%
+ * Create methods
+ */
+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;
+
+ stats = isc_mem_get(mctx, sizeof(*stats));
+ if (stats == NULL)
+ return (ISC_R_NOMEMORY);
+
+ stats->counters = NULL;
+ stats->references = 1;
+
+ result = isc_mutex_init(&stats->lock);
+ if (result != ISC_R_SUCCESS)
+ goto clean_stats;
+
+ result = isc_stats_create(mctx, &stats->counters, ncounters);
+ if (result != ISC_R_SUCCESS)
+ goto clean_mutex;
+
+ stats->magic = DNS_STATS_MAGIC;
+ stats->type = type;
+ stats->mctx = NULL;
+ isc_mem_attach(mctx, &stats->mctx);
+ *statsp = stats;
+
+ return (ISC_R_SUCCESS);
+
+ clean_mutex:
+ DESTROYLOCK(&stats->lock);
+ clean_stats:
+ isc_mem_put(mctx, stats, sizeof(*stats));
+
+ return (result);
+}
+
+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,
+ rdatasettypecounter_max, 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));
+}
+
+isc_result_t
+dns_rcodestats_create(isc_mem_t *mctx, dns_stats_t **statsp) {
+ REQUIRE(statsp != NULL && *statsp == NULL);
+
+ return (create_stats(mctx, dns_statstype_rcode,
+ dns_rcode_badcookie + 1, statsp));
+}
+
+/*%
+ * Increment/Decrement methods
+ */
+void
+dns_generalstats_increment(dns_stats_t *stats, isc_statscounter_t counter) {
+ REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_general);
+
+ isc_stats_increment(stats->counters, 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;
+
+ isc_stats_increment(stats->counters, (isc_statscounter_t)counter);
+}
+
+static inline void
+update_rdatasetstats(dns_stats_t *stats, dns_rdatastatstype_t rrsettype,
+ bool 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) {
+ if ((DNS_RDATASTATSTYPE_ATTR(rrsettype) &
+ DNS_RDATASTATSTYPE_ATTR_STALE) != 0) {
+ isc_stats_decrement(stats->counters, counter);
+ counter += rdtypecounter_stale;
+ }
+ isc_stats_increment(stats->counters, counter);
+ } else {
+ if ((DNS_RDATASTATSTYPE_ATTR(rrsettype) &
+ DNS_RDATASTATSTYPE_ATTR_STALE) != 0)
+ counter += rdtypecounter_stale;
+ isc_stats_decrement(stats->counters, 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, 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, false);
+}
+
+void
+dns_opcodestats_increment(dns_stats_t *stats, dns_opcode_t code) {
+ REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_opcode);
+
+ isc_stats_increment(stats->counters, (isc_statscounter_t)code);
+}
+
+void
+dns_rcodestats_increment(dns_stats_t *stats, dns_rcode_t code) {
+ REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_rcode);
+
+ if (code <= dns_rcode_badcookie)
+ isc_stats_increment(stats->counters, (isc_statscounter_t)code);
+}
+
+/*%
+ * Dump methods
+ */
+void
+dns_generalstats_dump(dns_stats_t *stats, dns_generalstats_dumper_t dump_fn,
+ void *arg, unsigned int options)
+{
+ REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_general);
+
+ isc_stats_dump(stats->counters, (isc_stats_dumper_t)dump_fn,
+ arg, options);
+}
+
+static void
+dump_rdentry(int rdcounter, uint64_t value, dns_rdatastatstype_t attributes,
+ dns_rdatatypestats_dumper_t dump_fn, void * arg)
+{
+ dns_rdatatype_t rdtype = dns_rdatatype_none; /* sentinel */
+ dns_rdatastatstype_t type;
+
+ 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, value, arg);
+}
+
+static void
+rdatatype_dumpcb(isc_statscounter_t counter, uint64_t value, void *arg) {
+ rdatadumparg_t *rdatadumparg = arg;
+
+ dump_rdentry(counter, value, 0, rdatadumparg->fn, rdatadumparg->arg);
+}
+
+void
+dns_rdatatypestats_dump(dns_stats_t *stats, dns_rdatatypestats_dumper_t dump_fn,
+ void *arg0, unsigned int options)
+{
+ rdatadumparg_t arg;
+ REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_rdtype);
+
+ arg.fn = dump_fn;
+ arg.arg = arg0;
+ isc_stats_dump(stats->counters, rdatatype_dumpcb, &arg, options);
+}
+
+static void
+rdataset_dumpcb(isc_statscounter_t counter, uint64_t value, void *arg) {
+ rdatadumparg_t *rdatadumparg = arg;
+ unsigned int attributes;
+
+ if (counter < rdtypecounter_max) {
+ dump_rdentry(counter, value, 0, rdatadumparg->fn,
+ rdatadumparg->arg);
+ } else if (counter < rdtypecounter_nxdomain) {
+ counter -= rdtypecounter_max;
+ attributes = DNS_RDATASTATSTYPE_ATTR_NXRRSET;
+ dump_rdentry(counter, value, attributes, rdatadumparg->fn,
+ rdatadumparg->arg);
+ } else if (counter == rdtypecounter_nxdomain) {
+ dump_rdentry(0, value, DNS_RDATASTATSTYPE_ATTR_NXDOMAIN,
+ rdatadumparg->fn, rdatadumparg->arg);
+ } else if (counter < rdtypecounter_stale + rdtypecounter_max) {
+ counter -= rdtypecounter_stale;
+ attributes = DNS_RDATASTATSTYPE_ATTR_STALE;
+ dump_rdentry(counter, value, attributes, rdatadumparg->fn,
+ rdatadumparg->arg);
+ } else if (counter < rdtypecounter_stale + rdtypecounter_nxdomain) {
+ counter -= rdtypecounter_stale + rdtypecounter_max;
+ attributes = DNS_RDATASTATSTYPE_ATTR_NXRRSET |
+ DNS_RDATASTATSTYPE_ATTR_STALE;
+ dump_rdentry(counter, value, attributes, rdatadumparg->fn,
+ rdatadumparg->arg);
+ } else {
+ attributes = DNS_RDATASTATSTYPE_ATTR_NXDOMAIN |
+ DNS_RDATASTATSTYPE_ATTR_STALE;
+ dump_rdentry(0, value, attributes, rdatadumparg->fn,
+ rdatadumparg->arg);
+ }
+}
+
+void
+dns_rdatasetstats_dump(dns_stats_t *stats, dns_rdatatypestats_dumper_t dump_fn,
+ void *arg0, unsigned int options)
+{
+ rdatadumparg_t arg;
+
+ REQUIRE(DNS_STATS_VALID(stats) &&
+ stats->type == dns_statstype_rdataset);
+
+ arg.fn = dump_fn;
+ arg.arg = arg0;
+ isc_stats_dump(stats->counters, rdataset_dumpcb, &arg, options);
+}
+
+static void
+opcode_dumpcb(isc_statscounter_t counter, uint64_t value, void *arg) {
+ opcodedumparg_t *opcodearg = arg;
+
+ opcodearg->fn((dns_opcode_t)counter, value, opcodearg->arg);
+}
+
+static void
+rcode_dumpcb(isc_statscounter_t counter, uint64_t value, void *arg) {
+ rcodedumparg_t *rcodearg = arg;
+
+ rcodearg->fn((dns_rcode_t)counter, value, rcodearg->arg);
+}
+
+void
+dns_opcodestats_dump(dns_stats_t *stats, dns_opcodestats_dumper_t dump_fn,
+ void *arg0, unsigned int options)
+{
+ opcodedumparg_t arg;
+
+ REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_opcode);
+
+ arg.fn = dump_fn;
+ arg.arg = arg0;
+ isc_stats_dump(stats->counters, opcode_dumpcb, &arg, options);
+}
+
+void
+dns_rcodestats_dump(dns_stats_t *stats, dns_rcodestats_dumper_t dump_fn,
+ void *arg0, unsigned int options)
+{
+ rcodedumparg_t arg;
+
+ REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_rcode);
+
+ arg.fn = dump_fn;
+ arg.arg = arg0;
+ isc_stats_dump(stats->counters, rcode_dumpcb, &arg, options);
+}
+
+/***
+ *** 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, uint64_t **ctrp) {
+ int i;
+ uint64_t *p =
+ isc_mem_get(mctx, DNS_STATS_NCOUNTERS * sizeof(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, uint64_t **ctrp) {
+ isc_mem_put(mctx, *ctrp, DNS_STATS_NCOUNTERS * sizeof(uint64_t));
+ *ctrp = NULL;
+}
diff --git a/lib/dns/tcpmsg.c b/lib/dns/tcpmsg.c
new file mode 100644
index 0000000..bb2a5d9
--- /dev/null
+++ b/lib/dns/tcpmsg.c
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <inttypes.h>
+
+#include <isc/mem.h>
+#include <isc/print.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 %u 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; /* 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/tests/Atffile b/lib/dns/tests/Atffile
new file mode 100644
index 0000000..953082d
--- /dev/null
+++ b/lib/dns/tests/Atffile
@@ -0,0 +1,34 @@
+Content-Type: application/X-atf-atffile; version="1"
+
+prop: test-suite = bind9
+
+tp: acl_test
+tp: db_test
+tp: dbdiff_test
+tp: dbiterator_test
+tp: dbversion_test
+tp: dh_test
+tp: dispatch_test
+tp: dnstap_test
+tp: dst_test
+tp: geoip_test
+tp: gost_test
+tp: keytable_test
+tp: master_test
+tp: name_test
+tp: nsec3_test
+tp: peer_test
+tp: private_test
+tp: rbt_serialize_test
+tp: rbt_test
+tp: rdata_test
+tp: rdataset_test
+tp: rdatasetstats_test
+tp: resolver_test
+tp: rsa_test
+tp: sigs_test
+tp: time_test
+tp: tsig_test
+tp: update_test
+tp: zonemgr_test
+tp: zt_test
diff --git a/lib/dns/tests/Kdh.+002+18602.key b/lib/dns/tests/Kdh.+002+18602.key
new file mode 100644
index 0000000..09b4cf5
--- /dev/null
+++ b/lib/dns/tests/Kdh.+002+18602.key
@@ -0,0 +1 @@
+dh. IN KEY 0 2 2 AAEBAAAAYIHI/wjtOagNga9GILSoS02IVelgLilPE/TfhtvShsiDAXqb IfxQcj2JkuOnNLs5ttb2WZXWl5/jsSjIxHMwMF2XY4gwt/lwHBf/vgYH r7aIxnKXov1jk9rymTLHGKIOtg==
diff --git a/lib/dns/tests/Krsa.+005+29235.key b/lib/dns/tests/Krsa.+005+29235.key
new file mode 100644
index 0000000..e2d81e7
--- /dev/null
+++ b/lib/dns/tests/Krsa.+005+29235.key
@@ -0,0 +1,5 @@
+; This is a zone-signing key, keyid 29235, for rsa.
+; Created: 20160819191802 (Fri Aug 19 21:18:02 2016)
+; Publish: 20160819191802 (Fri Aug 19 21:18:02 2016)
+; Activate: 20160819191802 (Fri Aug 19 21:18:02 2016)
+rsa. IN DNSKEY 256 3 5 AwEAAdLT1R3qiqCqll3Xzh2qFMvehQ9FODsPftw5U4UjB3QwnJ/3+dph 9kZBBeaJagUBVYzoArk6XNydpp3HhSCFDcIiepL6r8XAifW3SqI1KCne OD38kSCl/Qm9P0+3CFWokGVubsSQ+3dpQZxqx5bzOXthbuzAr6X+gDUE LAyHtCQNmJ+4ktdCoj3DNYW0z/xLvrcB2Lns7H+/qWnGPL4f3hr7Vbak Oeay+4J4KGdY2LFxJUVts6QrgAA8gz4mV9YIJFP+C4B3b/Z7qgqZRxmT 0pic+fJC5+sq0l8KwavPn0n+HqVuJNvppVKMdTbsmmuk69RFGMjbFkP7 tnCiqC9Zi6s=
diff --git a/lib/dns/tests/Kyuafile b/lib/dns/tests/Kyuafile
new file mode 100644
index 0000000..0353a73
--- /dev/null
+++ b/lib/dns/tests/Kyuafile
@@ -0,0 +1,33 @@
+syntax(2)
+test_suite('bind9')
+
+atf_test_program{name='acl_test'}
+atf_test_program{name='db_test'}
+atf_test_program{name='dbdiff_test'}
+atf_test_program{name='dbiterator_test'}
+atf_test_program{name='dbversion_test'}
+atf_test_program{name='dh_test'}
+atf_test_program{name='dispatch_test'}
+atf_test_program{name='dnstap_test'}
+atf_test_program{name='dst_test'}
+atf_test_program{name='geoip_test'}
+atf_test_program{name='gost_test'}
+atf_test_program{name='keytable_test'}
+atf_test_program{name='master_test'}
+atf_test_program{name='name_test'}
+atf_test_program{name='nsec3_test'}
+atf_test_program{name='peer_test'}
+atf_test_program{name='private_test'}
+atf_test_program{name='rbt_serialize_test', is_exclusive=true}
+atf_test_program{name='rbt_test'}
+atf_test_program{name='rdata_test'}
+atf_test_program{name='rdataset_test'}
+atf_test_program{name='rdatasetstats_test'}
+atf_test_program{name='resolver_test'}
+atf_test_program{name='rsa_test'}
+atf_test_program{name='sigs_test'}
+atf_test_program{name='time_test'}
+atf_test_program{name='tsig_test'}
+atf_test_program{name='update_test'}
+atf_test_program{name='zonemgr_test'}
+atf_test_program{name='zt_test'}
diff --git a/lib/dns/tests/Makefile.in b/lib/dns/tests/Makefile.in
new file mode 100644
index 0000000..58fa872
--- /dev/null
+++ b/lib/dns/tests/Makefile.in
@@ -0,0 +1,266 @@
+# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# See the COPYRIGHT file distributed with this work for additional
+# information regarding copyright ownership.
+
+srcdir = @srcdir@
+VPATH = @srcdir@
+top_srcdir = @top_srcdir@
+
+# Attempt to disable parallel processing.
+.NOTPARALLEL:
+.NO_PARALLEL:
+
+VERSION=@BIND9_VERSION@
+
+@BIND9_MAKE_INCLUDES@
+
+CINCLUDES = -I. -Iinclude ${DNS_INCLUDES} ${ISC_INCLUDES} \
+ @DST_OPENSSL_INC@
+CDEFINES = @CRYPTO@ -DTESTS="\"${top_builddir}/lib/dns/tests/\""
+
+ISCLIBS = ../../isc/libisc.@A@
+ISCDEPLIBS = ../../isc/libisc.@A@
+DNSLIBS = ../libdns.@A@ @DNS_CRYPTO_LIBS@
+DNSDEPLIBS = ../libdns.@A@
+
+LIBS = @LIBS@ @ATFLIBS@
+
+OBJS = dnstest.@O@
+SRCS = acl_test.c \
+ db_test.c \
+ dbdiff_test.c \
+ dbiterator_test.c \
+ dh_test.c \
+ dispatch_test.c \
+ dnstap_test.c \
+ dst_test.c \
+ dnstest.c \
+ geoip_test.c \
+ gost_test.c \
+ keytable_test.c \
+ master_test.c \
+ name_test.c \
+ nsec3_test.c \
+ peer_test.c \
+ private_test.c \
+ rbt_test.c \
+ rbt_serialize_test.c \
+ rdata_test.c \
+ rdataset_test.c \
+ rdatasetstats_test.c \
+ resolver_test.c \
+ rsa_test.c \
+ sigs_test.c \
+ time_test.c \
+ tsig_test.c \
+ update_test.c \
+ zonemgr_test.c \
+ zt_test.c
+
+SUBDIRS =
+TARGETS = acl_test@EXEEXT@ \
+ db_test@EXEEXT@ \
+ dbdiff_test@EXEEXT@ \
+ dbiterator_test@EXEEXT@ \
+ dbversion_test@EXEEXT@ \
+ dh_test@EXEEXT@ \
+ dispatch_test@EXEEXT@ \
+ dnstap_test@EXEEXT@ \
+ dst_test@EXEEXT@ \
+ geoip_test@EXEEXT@ \
+ gost_test@EXEEXT@ \
+ keytable_test@EXEEXT@ \
+ master_test@EXEEXT@ \
+ name_test@EXEEXT@ \
+ nsec3_test@EXEEXT@ \
+ peer_test@EXEEXT@ \
+ private_test@EXEEXT@ \
+ rbt_test@EXEEXT@ \
+ rbt_serialize_test@EXEEXT@ \
+ rdata_test@EXEEXT@ \
+ rdataset_test@EXEEXT@ \
+ rdatasetstats_test@EXEEXT@ \
+ resolver_test@EXEEXT@ \
+ rsa_test@EXEEXT@ \
+ sigs_test@EXEEXT@ \
+ time_test@EXEEXT@ \
+ tsig_test@EXEEXT@ \
+ update_test@EXEEXT@ \
+ zonemgr_test@EXEEXT@ \
+ zt_test@EXEEXT@
+
+@BIND9_MAKE_RULES@
+
+acl_test@EXEEXT@: acl_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
+ ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
+ acl_test.@O@ dnstest.@O@ ${DNSLIBS} \
+ ${ISCLIBS} ${LIBS}
+
+master_test@EXEEXT@: master_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
+ test -d testdata || mkdir testdata
+ test -d testdata/master || mkdir testdata/master
+ ${PERL} ${srcdir}/mkraw.pl < ${srcdir}/testdata/master/master12.data.in \
+ > testdata/master/master12.data
+ ${PERL} ${srcdir}/mkraw.pl < ${srcdir}/testdata/master/master13.data.in \
+ > testdata/master/master13.data
+ ${PERL} ${srcdir}/mkraw.pl < ${srcdir}/testdata/master/master14.data.in \
+ > testdata/master/master14.data
+ ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
+ master_test.@O@ dnstest.@O@ ${DNSLIBS} \
+ ${ISCLIBS} ${LIBS}
+
+keytable_test@EXEEXT@: keytable_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
+ ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
+ keytable_test.@O@ dnstest.@O@ ${DNSLIBS} \
+ ${ISCLIBS} ${LIBS}
+
+time_test@EXEEXT@: time_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
+ ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
+ time_test.@O@ dnstest.@O@ ${DNSLIBS} \
+ ${ISCLIBS} ${LIBS}
+
+peer_test@EXEEXT@: peer_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
+ ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
+ peer_test.@O@ dnstest.@O@ ${DNSLIBS} \
+ ${ISCLIBS} ${LIBS}
+
+private_test@EXEEXT@: private_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
+ ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
+ private_test.@O@ dnstest.@O@ ${DNSLIBS} \
+ ${ISCLIBS} ${LIBS}
+
+update_test@EXEEXT@: update_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
+ ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
+ update_test.@O@ dnstest.@O@ ${DNSLIBS} \
+ ${ISCLIBS} ${LIBS}
+
+zonemgr_test@EXEEXT@: zonemgr_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
+ ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
+ zonemgr_test.@O@ dnstest.@O@ ${DNSLIBS} \
+ ${ISCLIBS} ${LIBS}
+
+dbiterator_test@EXEEXT@: dbiterator_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
+ ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
+ dbiterator_test.@O@ dnstest.@O@ ${DNSLIBS} \
+ ${ISCLIBS} ${LIBS}
+
+dbdiff_test@EXEEXT@: dbdiff_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
+ ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
+ dbdiff_test.@O@ dnstest.@O@ ${DNSLIBS} \
+ ${ISCLIBS} ${LIBS}
+
+dbversion_test@EXEEXT@: dbversion_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
+ ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
+ dbversion_test.@O@ dnstest.@O@ ${DNSLIBS} \
+ ${ISCLIBS} ${LIBS}
+
+zt_test@EXEEXT@: zt_test.@O@ dnstest.@O@ \
+ ${ISCDEPLIBS} ${DNSDEPLIBS}
+ ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
+ zt_test.@O@ dnstest.@O@ ${DNSLIBS} \
+ ${ISCLIBS} ${LIBS}
+
+name_test@EXEEXT@: name_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
+ ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
+ name_test.@O@ dnstest.@O@ ${DNSLIBS} \
+ ${ISCLIBS} ${LIBS}
+
+nsec3_test@EXEEXT@: nsec3_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
+ ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
+ nsec3_test.@O@ dnstest.@O@ ${DNSLIBS} \
+ ${ISCLIBS} ${LIBS}
+
+rdataset_test@EXEEXT@: rdataset_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
+ ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
+ rdataset_test.@O@ dnstest.@O@ ${DNSLIBS} \
+ ${ISCLIBS} ${LIBS}
+
+dispatch_test@EXEEXT@: dispatch_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
+ ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
+ dispatch_test.@O@ dnstest.@O@ ${DNSLIBS} \
+ ${ISCLIBS} ${LIBS}
+
+dnstap_test@EXEEXT@: dnstap_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
+ ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
+ dnstap_test.@O@ dnstest.@O@ ${DNSLIBS} \
+ ${ISCLIBS} ${LIBS}
+
+rdatasetstats_test@EXEEXT@: rdatasetstats_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
+ ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
+ rdatasetstats_test.@O@ dnstest.@O@ ${DNSLIBS} \
+ ${ISCLIBS} ${LIBS}
+
+rbt_test@EXEEXT@: rbt_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
+ ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
+ rbt_test.@O@ dnstest.@O@ ${DNSLIBS} \
+ ${ISCLIBS} ${LIBS}
+
+rbt_serialize_test@EXEEXT@: rbt_serialize_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
+ ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
+ rbt_serialize_test.@O@ dnstest.@O@ ${DNSLIBS} \
+ ${ISCLIBS} ${LIBS}
+
+resolver_test@EXEEXT@: resolver_test.@O@ dnstest.@O@ \
+ ${ISCDEPLIBS} ${DNSDEPLIBS}
+ ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
+ resolver_test.@O@ dnstest.@O@ ${DNSLIBS} \
+ ${ISCLIBS} ${LIBS}
+
+rdata_test@EXEEXT@: rdata_test.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
+ ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
+ rdata_test.@O@ dnstest.@O@ ${DNSLIBS} \
+ ${ISCLIBS} ${LIBS}
+
+geoip_test@EXEEXT@: geoip_test.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
+ ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
+ geoip_test.@O@ dnstest.@O@ ${DNSLIBS} \
+ ${ISCLIBS} ${LIBS}
+sigs_test@EXEEXT@: sigs_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
+ ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
+ sigs_test.@O@ dnstest.@O@ ${DNSLIBS} \
+ ${ISCLIBS} ${LIBS}
+
+db_test@EXEEXT@: db_test.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
+ ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
+ db_test.@O@ dnstest.@O@ ${DNSLIBS} \
+ ${ISCLIBS} ${LIBS}
+
+gost_test@EXEEXT@: gost_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
+ ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
+ gost_test.@O@ dnstest.@O@ ${DNSLIBS} \
+ ${ISCLIBS} ${LIBS}
+
+dh_test@EXEEXT@: dh_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
+ ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
+ dh_test.@O@ dnstest.@O@ ${DNSLIBS} \
+ ${ISCLIBS} ${LIBS}
+
+dst_test@EXEEXT@: dst_test.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
+ ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
+ dst_test.@O@ dnstest.@O@ ${DNSLIBS} \
+ ${ISCLIBS} ${LIBS}
+
+rsa_test@EXEEXT@: rsa_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
+ ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
+ rsa_test.@O@ dnstest.@O@ ${DNSLIBS} \
+ ${ISCLIBS} ${LIBS}
+
+tsig_test@EXEEXT@: tsig_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
+ ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
+ tsig_test.@O@ dnstest.@O@ ${DNSLIBS} \
+ ${ISCLIBS} ${LIBS}
+
+unit::
+ sh ${top_builddir}/unit/unittest.sh
+
+clean distclean::
+ rm -f ${TARGETS}
+ rm -f atf.out
+ rm -f testdata/master/master12.data testdata/master/master13.data \
+ testdata/master/master14.data
+ rm -f zone.bin
diff --git a/lib/dns/tests/acl_test.c b/lib/dns/tests/acl_test.c
new file mode 100644
index 0000000..285a990
--- /dev/null
+++ b/lib/dns/tests/acl_test.c
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <atf-c.h>
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <unistd.h>
+
+#include <isc/print.h>
+#include <isc/string.h>
+
+#include <dns/acl.h>
+#include "dnstest.h"
+
+/*
+ * Helper functions
+ */
+
+#define BUFLEN 255
+#define BIGBUFLEN (70 * 1024)
+#define TEST_ORIGIN "test"
+
+ATF_TC(dns_acl_isinsecure);
+ATF_TC_HEAD(dns_acl_isinsecure, tc) {
+ atf_tc_set_md_var(tc, "descr", "test that dns_acl_isinsecure works");
+}
+ATF_TC_BODY(dns_acl_isinsecure, tc) {
+ isc_result_t result;
+ unsigned int pass;
+ struct {
+ bool first;
+ bool second;
+ } ecs[] = {
+ { false, false },
+ { true, true },
+ { true, false },
+ { false, true }
+ };
+
+ dns_acl_t *any = NULL;
+ dns_acl_t *none = NULL;
+ dns_acl_t *notnone = NULL;
+ dns_acl_t *notany = NULL;
+#ifdef HAVE_GEOIP
+ dns_acl_t *geoip = NULL;
+ dns_acl_t *notgeoip = NULL;
+ dns_aclelement_t *de;
+#endif
+
+ dns_acl_t *pos4pos6 = NULL;
+ dns_acl_t *notpos4pos6 = NULL;
+ dns_acl_t *neg4pos6 = NULL;
+ dns_acl_t *notneg4pos6 = NULL;
+ dns_acl_t *pos4neg6 = NULL;
+ dns_acl_t *notpos4neg6 = NULL;
+ dns_acl_t *neg4neg6 = NULL;
+ dns_acl_t *notneg4neg6 = NULL;
+
+ dns_acl_t *loop4 = NULL;
+ dns_acl_t *notloop4 = NULL;
+
+ dns_acl_t *loop6 = NULL;
+ dns_acl_t *notloop6 = NULL;
+
+ dns_acl_t *loop4pos6 = NULL;
+ dns_acl_t *notloop4pos6 = NULL;
+ dns_acl_t *loop4neg6 = NULL;
+ dns_acl_t *notloop4neg6 = NULL;
+
+ struct in_addr inaddr;
+ isc_netaddr_t addr;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_acl_any(mctx, &any);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_acl_none(mctx, &none);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_acl_create(mctx, 1, &notnone);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_acl_create(mctx, 1, &notany);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_acl_merge(notnone, none, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_acl_merge(notany, any, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+#ifdef HAVE_GEOIP
+ result = dns_acl_create(mctx, 1, &geoip);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ de = geoip->elements;
+ ATF_REQUIRE(de != NULL);
+ strlcpy(de->geoip_elem.as_string, "AU",
+ sizeof(de->geoip_elem.as_string));
+ de->geoip_elem.subtype = dns_geoip_country_code;
+ de->type = dns_aclelementtype_geoip;
+ de->negative = false;
+ ATF_REQUIRE(geoip->length < geoip->alloc);
+ geoip->node_count++;
+ de->node_num = geoip->node_count;
+ geoip->length++;
+
+ result = dns_acl_create(mctx, 1, &notgeoip);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_acl_merge(notgeoip, geoip, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+#endif
+
+ ATF_CHECK(dns_acl_isinsecure(any)); /* any; */
+ ATF_CHECK(!dns_acl_isinsecure(none)); /* none; */
+ ATF_CHECK(!dns_acl_isinsecure(notany)); /* !any; */
+ ATF_CHECK(!dns_acl_isinsecure(notnone)); /* !none; */
+
+#ifdef HAVE_GEOIP
+ ATF_CHECK(dns_acl_isinsecure(geoip)); /* geoip; */
+ ATF_CHECK(!dns_acl_isinsecure(notgeoip)); /* !geoip; */
+#endif
+
+ dns_acl_detach(&any);
+ dns_acl_detach(&none);
+ dns_acl_detach(&notany);
+ dns_acl_detach(&notnone);
+#ifdef HAVE_GEOIP
+ dns_acl_detach(&geoip);
+ dns_acl_detach(&notgeoip);
+#endif
+
+ for (pass = 0; pass < sizeof(ecs)/sizeof(ecs[0]); pass++) {
+ result = dns_acl_create(mctx, 1, &pos4pos6);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_acl_create(mctx, 1, &notpos4pos6);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_acl_create(mctx, 1, &neg4pos6);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_acl_create(mctx, 1, &notneg4pos6);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_acl_create(mctx, 1, &pos4neg6);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_acl_create(mctx, 1, &notpos4neg6);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_acl_create(mctx, 1, &neg4neg6);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_acl_create(mctx, 1, &notneg4neg6);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ inaddr.s_addr = htonl(0x0a000000); /* 10.0.0.0 */
+ isc_netaddr_fromin(&addr, &inaddr);
+ result = dns_iptable_addprefix2(pos4pos6->iptable, &addr, 8,
+ true, ecs[pass].first);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ addr.family = AF_INET6; /* 0a00:: */
+ result = dns_iptable_addprefix2(pos4pos6->iptable, &addr, 8,
+ true, ecs[pass].second);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_acl_merge(notpos4pos6, pos4pos6, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ inaddr.s_addr = htonl(0x0a000000); /* !10.0.0.0/8 */
+ isc_netaddr_fromin(&addr, &inaddr);
+ result = dns_iptable_addprefix2(neg4pos6->iptable, &addr, 8,
+ false, ecs[pass].first);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ addr.family = AF_INET6; /* 0a00::/8 */
+ result = dns_iptable_addprefix2(neg4pos6->iptable, &addr, 8,
+ true, ecs[pass].second);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_acl_merge(notneg4pos6, neg4pos6, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ inaddr.s_addr = htonl(0x0a000000); /* 10.0.0.0/8 */
+ isc_netaddr_fromin(&addr, &inaddr);
+ result = dns_iptable_addprefix2(pos4neg6->iptable, &addr, 8,
+ true, ecs[pass].first);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ addr.family = AF_INET6; /* !0a00::/8 */
+ result = dns_iptable_addprefix2(pos4neg6->iptable, &addr, 8,
+ false, ecs[pass].second);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_acl_merge(notpos4neg6, pos4neg6, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ inaddr.s_addr = htonl(0x0a000000); /* !10.0.0.0/8 */
+ isc_netaddr_fromin(&addr, &inaddr);
+ result = dns_iptable_addprefix2(neg4neg6->iptable, &addr, 8,
+ false, ecs[pass].first);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ addr.family = AF_INET6; /* !0a00::/8 */
+ result = dns_iptable_addprefix2(neg4neg6->iptable, &addr, 8,
+ false, ecs[pass].second);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_acl_merge(notneg4neg6, neg4neg6, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ ATF_CHECK(dns_acl_isinsecure(pos4pos6));
+ ATF_CHECK(!dns_acl_isinsecure(notpos4pos6));
+ ATF_CHECK(dns_acl_isinsecure(neg4pos6));
+ ATF_CHECK(!dns_acl_isinsecure(notneg4pos6));
+ ATF_CHECK(dns_acl_isinsecure(pos4neg6));
+ ATF_CHECK(!dns_acl_isinsecure(notpos4neg6));
+ ATF_CHECK(!dns_acl_isinsecure(neg4neg6));
+ ATF_CHECK(!dns_acl_isinsecure(notneg4neg6));
+
+ dns_acl_detach(&pos4pos6);
+ dns_acl_detach(&notpos4pos6);
+ dns_acl_detach(&neg4pos6);
+ dns_acl_detach(&notneg4pos6);
+ dns_acl_detach(&pos4neg6);
+ dns_acl_detach(&notpos4neg6);
+ dns_acl_detach(&neg4neg6);
+ dns_acl_detach(&notneg4neg6);
+
+ result = dns_acl_create(mctx, 1, &loop4);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_acl_create(mctx, 1, &notloop4);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_acl_create(mctx, 1, &loop6);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_acl_create(mctx, 1, &notloop6);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ inaddr.s_addr = htonl(0x7f000001); /* 127.0.0.1 */
+ isc_netaddr_fromin(&addr, &inaddr);
+ result = dns_iptable_addprefix2(loop4->iptable, &addr, 32,
+ true, ecs[pass].first);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_acl_merge(notloop4, loop4, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ isc_netaddr_fromin6(&addr, &in6addr_loopback); /* ::1 */
+ result = dns_iptable_addprefix2(loop6->iptable, &addr, 128,
+ true, ecs[pass].first);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_acl_merge(notloop6, loop6, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ if (!ecs[pass].first) {
+ ATF_CHECK(!dns_acl_isinsecure(loop4));
+ ATF_CHECK(!dns_acl_isinsecure(notloop4));
+ ATF_CHECK(!dns_acl_isinsecure(loop6));
+ ATF_CHECK(!dns_acl_isinsecure(notloop6));
+ } else if (ecs[pass].first) {
+ ATF_CHECK(dns_acl_isinsecure(loop4));
+ ATF_CHECK(!dns_acl_isinsecure(notloop4));
+ ATF_CHECK(dns_acl_isinsecure(loop6));
+ ATF_CHECK(!dns_acl_isinsecure(notloop6));
+ }
+
+ dns_acl_detach(&loop4);
+ dns_acl_detach(&notloop4);
+ dns_acl_detach(&loop6);
+ dns_acl_detach(&notloop6);
+
+ result = dns_acl_create(mctx, 1, &loop4pos6);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_acl_create(mctx, 1, &notloop4pos6);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_acl_create(mctx, 1, &loop4neg6);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_acl_create(mctx, 1, &notloop4neg6);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ inaddr.s_addr = htonl(0x7f000001); /* 127.0.0.1 */
+ isc_netaddr_fromin(&addr, &inaddr);
+ result = dns_iptable_addprefix2(loop4pos6->iptable, &addr, 32,
+ true, ecs[pass].first);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ addr.family = AF_INET6; /* f700:0001::/32 */
+ result = dns_iptable_addprefix2(loop4pos6->iptable, &addr, 32,
+ true, ecs[pass].second);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_acl_merge(notloop4pos6, loop4pos6, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ inaddr.s_addr = htonl(0x7f000001); /* 127.0.0.1 */
+ isc_netaddr_fromin(&addr, &inaddr);
+ result = dns_iptable_addprefix2(loop4neg6->iptable, &addr, 32,
+ true, ecs[pass].first);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ addr.family = AF_INET6; /* !f700:0001::/32 */
+ result = dns_iptable_addprefix2(loop4neg6->iptable, &addr, 32,
+ false, ecs[pass].second);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_acl_merge(notloop4neg6, loop4neg6, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ if (!ecs[pass].first && !ecs[pass].second) {
+ ATF_CHECK(dns_acl_isinsecure(loop4pos6));
+ ATF_CHECK(!dns_acl_isinsecure(notloop4pos6));
+ ATF_CHECK(!dns_acl_isinsecure(loop4neg6));
+ ATF_CHECK(!dns_acl_isinsecure(notloop4neg6));
+ } else if (ecs[pass].first && !ecs[pass].second) {
+ ATF_CHECK(dns_acl_isinsecure(loop4pos6));
+ ATF_CHECK(!dns_acl_isinsecure(notloop4pos6));
+ ATF_CHECK(dns_acl_isinsecure(loop4neg6));
+ ATF_CHECK(!dns_acl_isinsecure(notloop4neg6));
+ } else if (!ecs[pass].first && ecs[pass].second) {
+ ATF_CHECK(dns_acl_isinsecure(loop4pos6));
+ ATF_CHECK(!dns_acl_isinsecure(notloop4pos6));
+ ATF_CHECK(!dns_acl_isinsecure(loop4neg6));
+ ATF_CHECK(!dns_acl_isinsecure(notloop4neg6));
+ } else {
+ ATF_CHECK(dns_acl_isinsecure(loop4pos6));
+ ATF_CHECK(!dns_acl_isinsecure(notloop4pos6));
+ ATF_CHECK(dns_acl_isinsecure(loop4neg6));
+ ATF_CHECK(!dns_acl_isinsecure(notloop4neg6));
+ }
+
+ dns_acl_detach(&loop4pos6);
+ dns_acl_detach(&notloop4pos6);
+ dns_acl_detach(&loop4neg6);
+ dns_acl_detach(&notloop4neg6);
+ }
+
+ dns_test_end();
+}
+
+/*
+ * Main
+ */
+ATF_TP_ADD_TCS(tp) {
+ ATF_TP_ADD_TC(tp, dns_acl_isinsecure);
+ return (atf_no_error());
+}
diff --git a/lib/dns/tests/db_test.c b/lib/dns/tests/db_test.c
new file mode 100644
index 0000000..d12a497
--- /dev/null
+++ b/lib/dns/tests/db_test.c
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <atf-c.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <dns/db.h>
+#include <dns/dbiterator.h>
+#include <dns/name.h>
+#include <dns/journal.h>
+
+#include "dnstest.h"
+
+/*
+ * Helper functions
+ */
+
+#define BUFLEN 255
+#define BIGBUFLEN (64 * 1024)
+#define TEST_ORIGIN "test"
+
+/*
+ * Individual unit tests
+ */
+
+ATF_TC(getoriginnode);
+ATF_TC_HEAD(getoriginnode, tc) {
+ atf_tc_set_md_var(tc, "descr",
+ "test multiple calls to dns_db_getoriginnode");
+}
+ATF_TC_BODY(getoriginnode, tc) {
+ dns_db_t *db = NULL;
+ dns_dbnode_t *node = NULL;
+ isc_mem_t *mymctx = NULL;
+ isc_result_t result;
+
+ result = isc_mem_create(0, 0, &mymctx);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = isc_hash_create(mymctx, NULL, 256);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_db_create(mymctx, "rbt", dns_rootname, dns_dbtype_zone,
+ dns_rdataclass_in, 0, NULL, &db);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_db_getoriginnode(db, &node);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ dns_db_detachnode(db, &node);
+
+ result = dns_db_getoriginnode(db, &node);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ dns_db_detachnode(db, &node);
+
+ dns_db_detach(&db);
+ isc_mem_detach(&mymctx);
+}
+
+ATF_TC(class);
+ATF_TC_HEAD(class, tc) {
+ atf_tc_set_md_var(tc, "descr", "database class");
+}
+ATF_TC_BODY(class, tc) {
+ isc_result_t result;
+ dns_db_t *db = NULL;
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_db_create(mctx, "rbt", dns_rootname, dns_dbtype_zone,
+ dns_rdataclass_in, 0, NULL, &db);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_db_load(db, "testdata/db/data.db");
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ ATF_CHECK_EQ(dns_db_class(db), dns_rdataclass_in);
+
+ dns_db_detach(&db);
+}
+
+ATF_TC(dbtype);
+ATF_TC_HEAD(dbtype, tc) {
+ atf_tc_set_md_var(tc, "descr", "database type");
+}
+ATF_TC_BODY(dbtype, tc) {
+ isc_result_t result;
+ dns_db_t *db = NULL;
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ /* DB has zone semantics */
+ result = dns_db_create(mctx, "rbt", dns_rootname, dns_dbtype_zone,
+ dns_rdataclass_in, 0, NULL, &db);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ result = dns_db_load(db, "testdata/db/data.db");
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ ATF_CHECK(dns_db_iszone(db));
+ ATF_CHECK(!dns_db_iscache(db));
+ dns_db_detach(&db);
+
+ /* DB has cache semantics */
+ result = dns_db_create(mctx, "rbt", dns_rootname, dns_dbtype_cache,
+ dns_rdataclass_in, 0, NULL, &db);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ result = dns_db_load(db, "testdata/db/data.db");
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ ATF_CHECK(dns_db_iscache(db));
+ ATF_CHECK(!dns_db_iszone(db));
+ dns_db_detach(&db);
+
+ dns_test_end();
+}
+
+ATF_TC(version);
+ATF_TC_HEAD(version, tc) {
+ atf_tc_set_md_var(tc, "descr", "database versions");
+}
+ATF_TC_BODY(version, tc) {
+ isc_result_t result;
+ dns_fixedname_t fname, ffound;
+ dns_name_t *name, *foundname;
+ dns_db_t *db = NULL;
+ dns_dbversion_t *ver = NULL, *new = NULL;
+ dns_dbnode_t *node = NULL;
+ dns_rdataset_t rdataset;
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_test_loaddb(&db, dns_dbtype_zone, "test.test",
+ "testdata/db/data.db");
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ /* Open current version for reading */
+ dns_db_currentversion(db, &ver);
+ dns_test_namefromstring("b.test.test", &fname);
+ name = dns_fixedname_name(&fname);
+ foundname = dns_fixedname_initname(&ffound);
+ dns_rdataset_init(&rdataset);
+ result = dns_db_find(db, name , ver, dns_rdatatype_a, 0, 0, &node,
+ foundname, &rdataset, NULL);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ dns_rdataset_disassociate(&rdataset);
+ dns_db_detachnode(db, &node);
+ dns_db_closeversion(db, &ver, false);
+
+ /* Open new version for writing */
+ dns_db_currentversion(db, &ver);
+ dns_test_namefromstring("b.test.test", &fname);
+ name = dns_fixedname_name(&fname);
+ foundname = dns_fixedname_initname(&ffound);
+ dns_rdataset_init(&rdataset);
+ result = dns_db_find(db, name , ver, dns_rdatatype_a, 0, 0, &node,
+ foundname, &rdataset, NULL);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_db_newversion(db, &new);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ /* Delete the rdataset from the new verison */
+ result = dns_db_deleterdataset(db, node, new, dns_rdatatype_a, 0);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ dns_rdataset_disassociate(&rdataset);
+ dns_db_detachnode(db, &node);
+
+ /* This should fail now */
+ result = dns_db_find(db, name, new, dns_rdatatype_a, 0, 0, &node,
+ foundname, &rdataset, NULL);
+ ATF_REQUIRE_EQ(result, DNS_R_NXDOMAIN);
+
+ dns_db_closeversion(db, &new, true);
+
+ /* But this should still succeed */
+ result = dns_db_find(db, name, ver, dns_rdatatype_a, 0, 0, &node,
+ foundname, &rdataset, NULL);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ dns_rdataset_disassociate(&rdataset);
+ dns_db_detachnode(db, &node);
+ dns_db_closeversion(db, &ver, false);
+
+ dns_db_detach(&db);
+ dns_test_end();
+}
+
+/*
+ * Main
+ */
+ATF_TP_ADD_TCS(tp) {
+ ATF_TP_ADD_TC(tp, getoriginnode);
+ ATF_TP_ADD_TC(tp, class);
+ ATF_TP_ADD_TC(tp, dbtype);
+ ATF_TP_ADD_TC(tp, version);
+
+ return (atf_no_error());
+}
diff --git a/lib/dns/tests/dbdiff_test.c b/lib/dns/tests/dbdiff_test.c
new file mode 100644
index 0000000..2ddd243
--- /dev/null
+++ b/lib/dns/tests/dbdiff_test.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <atf-c.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <dns/db.h>
+#include <dns/dbiterator.h>
+#include <dns/name.h>
+#include <dns/journal.h>
+
+#include "dnstest.h"
+
+/*
+ * Helper functions
+ */
+
+#define BUFLEN 255
+#define BIGBUFLEN (64 * 1024)
+#define TEST_ORIGIN "test"
+
+static void
+test_create(const atf_tc_t *tc, dns_db_t **old, dns_db_t **newdb) {
+ isc_result_t result;
+
+ result = dns_test_loaddb(old, dns_dbtype_zone, TEST_ORIGIN,
+ atf_tc_get_md_var(tc, "X-old"));
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_test_loaddb(newdb, dns_dbtype_zone, TEST_ORIGIN,
+ atf_tc_get_md_var(tc, "X-new"));
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+}
+
+/*
+ * Individual unit tests
+ */
+
+ATF_TC(diffx_same);
+ATF_TC_HEAD(diffx_same, tc) {
+ atf_tc_set_md_var(tc, "descr", "dns_db_diffx of identical content");
+ atf_tc_set_md_var(tc, "X-old", "testdata/diff/zone1.data");
+ atf_tc_set_md_var(tc, "X-new", "testdata/diff/zone1.data"); }
+ATF_TC_BODY(diffx_same, tc) {
+ dns_db_t *newdb = NULL, *olddb = NULL;
+ isc_result_t result;
+ dns_diff_t diff;
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ test_create(tc, &olddb, &newdb);
+
+ dns_diff_init(mctx, &diff);
+
+ result = dns_db_diffx(&diff, newdb, NULL, olddb, NULL, NULL);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ ATF_REQUIRE_EQ(ISC_LIST_EMPTY(diff.tuples), true);
+
+ dns_diff_clear(&diff);
+ dns_db_detach(&newdb);
+ dns_db_detach(&olddb);
+ dns_test_end();
+}
+
+ATF_TC(diffx_add);
+ATF_TC_HEAD(diffx_add, tc) {
+ atf_tc_set_md_var(tc, "descr",
+ "dns_db_diffx of zone with record added");
+ atf_tc_set_md_var(tc, "X-old", "testdata/diff/zone1.data");
+ atf_tc_set_md_var(tc, "X-new", "testdata/diff/zone2.data");
+}
+ATF_TC_BODY(diffx_add, tc) {
+ dns_db_t *newdb = NULL, *olddb = NULL;
+ dns_difftuple_t *tuple;
+ isc_result_t result;
+ dns_diff_t diff;
+ int count = 0;
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ test_create(tc, &olddb, &newdb);
+
+ dns_diff_init(mctx, &diff);
+
+ result = dns_db_diffx(&diff, newdb, NULL, olddb, NULL, NULL);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ ATF_REQUIRE_EQ(ISC_LIST_EMPTY(diff.tuples), false);
+ for (tuple = ISC_LIST_HEAD(diff.tuples); tuple != NULL;
+ tuple = ISC_LIST_NEXT(tuple, link)) {
+ ATF_REQUIRE_EQ(tuple->op, DNS_DIFFOP_ADD);
+ count++;
+ }
+ ATF_REQUIRE_EQ(count, 1);
+
+ dns_diff_clear(&diff);
+ dns_db_detach(&newdb);
+ dns_db_detach(&olddb);
+ dns_test_end();
+}
+
+ATF_TC(diffx_remove);
+ATF_TC_HEAD(diffx_remove, tc) {
+ atf_tc_set_md_var(tc, "descr",
+ "dns_db_diffx of zone with record removed");
+ atf_tc_set_md_var(tc, "X-old", "testdata/diff/zone1.data");
+ atf_tc_set_md_var(tc, "X-new", "testdata/diff/zone3.data");
+}
+ATF_TC_BODY(diffx_remove, tc) {
+ dns_db_t *newdb = NULL, *olddb = NULL;
+ dns_difftuple_t *tuple;
+ isc_result_t result;
+ dns_diff_t diff;
+ int count = 0;
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ test_create(tc, &olddb, &newdb);
+
+ dns_diff_init(mctx, &diff);
+
+ result = dns_db_diffx(&diff, newdb, NULL, olddb, NULL, NULL);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ ATF_REQUIRE_EQ(ISC_LIST_EMPTY(diff.tuples), false);
+ for (tuple = ISC_LIST_HEAD(diff.tuples); tuple != NULL;
+ tuple = ISC_LIST_NEXT(tuple, link)) {
+ ATF_REQUIRE_EQ(tuple->op, DNS_DIFFOP_DEL);
+ count++;
+ }
+ ATF_REQUIRE_EQ(count, 1);
+
+ dns_diff_clear(&diff);
+ dns_db_detach(&newdb);
+ dns_db_detach(&olddb);
+ dns_test_end();
+}
+
+/*
+ * Main
+ */
+ATF_TP_ADD_TCS(tp) {
+ ATF_TP_ADD_TC(tp, diffx_same);
+ ATF_TP_ADD_TC(tp, diffx_add);
+ ATF_TP_ADD_TC(tp, diffx_remove);
+ return (atf_no_error());
+}
diff --git a/lib/dns/tests/dbiterator_test.c b/lib/dns/tests/dbiterator_test.c
new file mode 100644
index 0000000..643627b
--- /dev/null
+++ b/lib/dns/tests/dbiterator_test.c
@@ -0,0 +1,425 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <atf-c.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <dns/db.h>
+#include <dns/dbiterator.h>
+#include <dns/name.h>
+
+#include "dnstest.h"
+
+/*
+ * Helper functions
+ */
+
+#define BUFLEN 255
+#define BIGBUFLEN (64 * 1024)
+#define TEST_ORIGIN "test"
+
+static isc_result_t
+make_name(const char *src, dns_name_t *name) {
+ isc_buffer_t b;
+ isc_buffer_constinit(&b, src, strlen(src));
+ isc_buffer_add(&b, strlen(src));
+ return (dns_name_fromtext(name, &b, dns_rootname, 0, NULL));
+}
+
+/*
+ * Individual unit tests
+ */
+
+/* create: make sure we can create a dbiterator */
+static void
+test_create(const atf_tc_t *tc) {
+ isc_result_t result;
+ dns_db_t *db = NULL;
+ dns_dbiterator_t *iter = NULL;
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_test_loaddb(&db, dns_dbtype_cache, TEST_ORIGIN,
+ atf_tc_get_md_var(tc, "X-filename"));
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_db_createiterator(db, 0, &iter);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ dns_dbiterator_destroy(&iter);
+ dns_db_detach(&db);
+ dns_test_end();
+}
+
+ATF_TC(create);
+ATF_TC_HEAD(create, tc) {
+ atf_tc_set_md_var(tc, "descr", "create a database iterator");
+ atf_tc_set_md_var(tc, "X-filename", "testdata/dbiterator/zone1.data");
+}
+ATF_TC_BODY(create, tc) {
+ test_create(tc);
+}
+
+ATF_TC(create_nsec3);
+ATF_TC_HEAD(create_nsec3, tc) {
+ atf_tc_set_md_var(tc, "descr", "create a database iterator (NSEC3)");
+ atf_tc_set_md_var(tc, "X-filename", "testdata/dbiterator/zone2.data");
+}
+ATF_TC_BODY(create_nsec3, tc) {
+ test_create(tc);
+}
+
+/* walk: walk a database */
+static void
+test_walk(const atf_tc_t *tc) {
+ isc_result_t result;
+ dns_db_t *db = NULL;
+ dns_dbiterator_t *iter = NULL;
+ dns_dbnode_t *node = NULL;
+ dns_name_t *name;
+ dns_fixedname_t f;
+ int i = 0;
+
+ UNUSED(tc);
+
+ name = dns_fixedname_initname(&f);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_test_loaddb(&db, dns_dbtype_cache, TEST_ORIGIN,
+ atf_tc_get_md_var(tc, "X-filename"));
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_db_createiterator(db, 0, &iter);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ for (result = dns_dbiterator_first(iter);
+ result == ISC_R_SUCCESS;
+ result = dns_dbiterator_next(iter)) {
+ result = dns_dbiterator_current(iter, &node, name);
+ if (result == DNS_R_NEWORIGIN)
+ result = ISC_R_SUCCESS;
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ dns_db_detachnode(db, &node);
+ i++;
+ }
+
+ ATF_CHECK_EQ(i, atoi(atf_tc_get_md_var(tc, "X-nodes")));
+
+ dns_dbiterator_destroy(&iter);
+ dns_db_detach(&db);
+ dns_test_end();
+}
+
+ATF_TC(walk);
+ATF_TC_HEAD(walk, tc) {
+ atf_tc_set_md_var(tc, "descr", "walk database");
+ atf_tc_set_md_var(tc, "X-filename", "testdata/dbiterator/zone1.data");
+ atf_tc_set_md_var(tc, "X-nodes", "12");
+}
+ATF_TC_BODY(walk, tc) {
+ test_walk(tc);
+}
+
+ATF_TC(walk_nsec3);
+ATF_TC_HEAD(walk_nsec3, tc) {
+ atf_tc_set_md_var(tc, "descr", "walk database");
+ atf_tc_set_md_var(tc, "X-filename", "testdata/dbiterator/zone2.data");
+ atf_tc_set_md_var(tc, "X-nodes", "33");
+}
+ATF_TC_BODY(walk_nsec3, tc) {
+ test_walk(tc);
+}
+
+/* reverse: walk database backwards */
+static void test_reverse(const atf_tc_t *tc) {
+ isc_result_t result;
+ dns_db_t *db = NULL;
+ dns_dbiterator_t *iter = NULL;
+ dns_dbnode_t *node = NULL;
+ dns_name_t *name;
+ dns_fixedname_t f;
+ int i = 0;
+
+ UNUSED(tc);
+
+ name = dns_fixedname_initname(&f);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_test_loaddb(&db, dns_dbtype_cache, TEST_ORIGIN,
+ atf_tc_get_md_var(tc, "X-filename"));
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_db_createiterator(db, 0, &iter);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ for (result = dns_dbiterator_last(iter);
+ result == ISC_R_SUCCESS;
+ result = dns_dbiterator_prev(iter)) {
+ result = dns_dbiterator_current(iter, &node, name);
+ if (result == DNS_R_NEWORIGIN)
+ result = ISC_R_SUCCESS;
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ dns_db_detachnode(db, &node);
+ i++;
+ }
+
+ ATF_CHECK_EQ(i, 12);
+
+ dns_dbiterator_destroy(&iter);
+ dns_db_detach(&db);
+ dns_test_end();
+}
+
+ATF_TC(reverse);
+ATF_TC_HEAD(reverse, tc) {
+ atf_tc_set_md_var(tc, "descr", "walk database backwards");
+ atf_tc_set_md_var(tc, "X-filename", "testdata/dbiterator/zone1.data");
+}
+ATF_TC_BODY(reverse, tc) {
+ test_reverse(tc);
+}
+
+ATF_TC(reverse_nsec3);
+ATF_TC_HEAD(reverse_nsec3, tc) {
+ atf_tc_set_md_var(tc, "descr", "walk database backwards");
+ atf_tc_set_md_var(tc, "X-filename", "testdata/dbiterator/zone2.data");
+}
+ATF_TC_BODY(reverse_nsec3, tc) {
+ test_reverse(tc);
+}
+
+/* seek: walk database starting at a particular node */
+static void test_seek(const atf_tc_t *tc) {
+ isc_result_t result;
+ dns_db_t *db = NULL;
+ dns_dbiterator_t *iter = NULL;
+ dns_dbnode_t *node = NULL;
+ dns_name_t *name, *seekname;
+ dns_fixedname_t f1, f2;
+ int i = 0;
+
+ UNUSED(tc);
+
+ name = dns_fixedname_initname(&f1);
+ seekname = dns_fixedname_initname(&f2);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_test_loaddb(&db, dns_dbtype_cache, TEST_ORIGIN,
+ atf_tc_get_md_var(tc, "X-filename"));
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_db_createiterator(db, 0, &iter);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = make_name("c." TEST_ORIGIN, seekname);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_dbiterator_seek(iter, seekname);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ while (result == ISC_R_SUCCESS) {
+ result = dns_dbiterator_current(iter, &node, name);
+ if (result == DNS_R_NEWORIGIN)
+ result = ISC_R_SUCCESS;
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ dns_db_detachnode(db, &node);
+ result = dns_dbiterator_next(iter);
+ i++;
+ }
+
+ ATF_CHECK_EQ(i, atoi(atf_tc_get_md_var(tc, "X-nodes")));
+
+ dns_dbiterator_destroy(&iter);
+ dns_db_detach(&db);
+ dns_test_end();
+}
+
+ATF_TC(seek);
+ATF_TC_HEAD(seek, tc) {
+ atf_tc_set_md_var(tc, "descr", "walk database starting at "
+ "a particular node");
+ atf_tc_set_md_var(tc, "X-filename", "testdata/dbiterator/zone1.data");
+ atf_tc_set_md_var(tc, "X-nodes", "9");
+}
+ATF_TC_BODY(seek, tc) {
+ test_seek(tc);
+}
+
+ATF_TC(seek_nsec3);
+ATF_TC_HEAD(seek_nsec3, tc) {
+ atf_tc_set_md_var(tc, "descr", "walk database starting at "
+ "a particular node");
+ atf_tc_set_md_var(tc, "X-filename", "testdata/dbiterator/zone2.data");
+ atf_tc_set_md_var(tc, "X-nodes", "30");
+}
+ATF_TC_BODY(seek_nsec3, tc) {
+ test_seek(tc);
+}
+
+/*
+ * seek_emty: walk database starting at an empty nonterminal node
+ * (should fail)
+ */
+static void test_seek_empty(const atf_tc_t *tc) {
+ isc_result_t result;
+ dns_db_t *db = NULL;
+ dns_dbiterator_t *iter = NULL;
+ dns_name_t *seekname;
+ dns_fixedname_t f1;
+
+ UNUSED(tc);
+
+ seekname = dns_fixedname_initname(&f1);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_test_loaddb(&db, dns_dbtype_cache, TEST_ORIGIN,
+ atf_tc_get_md_var(tc, "X-filename"));
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_db_createiterator(db, 0, &iter);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = make_name("d." TEST_ORIGIN, seekname);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_dbiterator_seek(iter, seekname);
+ ATF_CHECK_EQ(result, DNS_R_PARTIALMATCH);
+
+ dns_dbiterator_destroy(&iter);
+ dns_db_detach(&db);
+ dns_test_end();
+}
+
+ATF_TC(seek_empty);
+ATF_TC_HEAD(seek_empty, tc) {
+ atf_tc_set_md_var(tc, "descr", "walk database starting at an "
+ "empty nonterminal node");
+ atf_tc_set_md_var(tc, "X-filename", "testdata/dbiterator/zone1.data");
+}
+ATF_TC_BODY(seek_empty, tc) {
+ test_seek_empty(tc);
+}
+
+ATF_TC(seek_empty_nsec3);
+ATF_TC_HEAD(seek_empty_nsec3, tc) {
+ atf_tc_set_md_var(tc, "descr", "walk database starting at an "
+ "empty nonterminal node");
+ atf_tc_set_md_var(tc, "X-filename", "testdata/dbiterator/zone2.data");
+}
+ATF_TC_BODY(seek_empty_nsec3, tc) {
+ test_seek_empty(tc);
+}
+
+/*
+ * seek_emty: walk database starting at an empty nonterminal node
+ * (should fail)
+ */
+static void test_seek_nx(const atf_tc_t *tc) {
+ isc_result_t result;
+ dns_db_t *db = NULL;
+ dns_dbiterator_t *iter = NULL;
+ dns_name_t *seekname;
+ dns_fixedname_t f1;
+
+ UNUSED(tc);
+
+ seekname = dns_fixedname_initname(&f1);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_test_loaddb(&db, dns_dbtype_cache, TEST_ORIGIN,
+ atf_tc_get_md_var(tc, "X-filename"));
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_db_createiterator(db, 0, &iter);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = make_name("nonexistent." TEST_ORIGIN, seekname);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_dbiterator_seek(iter, seekname);
+ ATF_CHECK_EQ(result, DNS_R_PARTIALMATCH);
+
+ result = make_name("nonexistent.", seekname);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_dbiterator_seek(iter, seekname);
+ ATF_CHECK_EQ(result, ISC_R_NOTFOUND);
+
+ dns_dbiterator_destroy(&iter);
+ dns_db_detach(&db);
+ dns_test_end();
+}
+
+ATF_TC(seek_nx);
+ATF_TC_HEAD(seek_nx, tc) {
+ atf_tc_set_md_var(tc, "descr", "attempt to walk database starting "
+ "at a nonexistent node");
+ atf_tc_set_md_var(tc, "X-filename", "testdata/dbiterator/zone1.data");
+}
+ATF_TC_BODY(seek_nx, tc) {
+ test_seek_nx(tc);
+}
+
+ATF_TC(seek_nx_nsec3);
+ATF_TC_HEAD(seek_nx_nsec3, tc) {
+ atf_tc_set_md_var(tc, "descr", "attempt to walk database starting "
+ "at a nonexistent node");
+ atf_tc_set_md_var(tc, "X-filename", "testdata/dbiterator/zone2.data");
+}
+ATF_TC_BODY(seek_nx_nsec3, tc) {
+ test_seek_nx(tc);
+}
+
+/*
+ * Main
+ */
+ATF_TP_ADD_TCS(tp) {
+ ATF_TP_ADD_TC(tp, create);
+ ATF_TP_ADD_TC(tp, create_nsec3);
+ ATF_TP_ADD_TC(tp, walk);
+ ATF_TP_ADD_TC(tp, walk_nsec3);
+ ATF_TP_ADD_TC(tp, reverse);
+ ATF_TP_ADD_TC(tp, reverse_nsec3);
+ ATF_TP_ADD_TC(tp, seek);
+ ATF_TP_ADD_TC(tp, seek_nsec3);
+ ATF_TP_ADD_TC(tp, seek_empty);
+ ATF_TP_ADD_TC(tp, seek_empty_nsec3);
+ ATF_TP_ADD_TC(tp, seek_nx);
+ ATF_TP_ADD_TC(tp, seek_nx_nsec3);
+ return (atf_no_error());
+}
+
+/*
+ * XXX:
+ * dns_dbiterator API calls that are not yet part of this unit test:
+ *
+ * dns_dbiterator_pause
+ * dns_dbiterator_origin
+ * dns_dbiterator_setcleanmode
+ */
diff --git a/lib/dns/tests/dbversion_test.c b/lib/dns/tests/dbversion_test.c
new file mode 100644
index 0000000..275fc9b
--- /dev/null
+++ b/lib/dns/tests/dbversion_test.c
@@ -0,0 +1,735 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#include <isc/file.h>
+#include <isc/result.h>
+#include <isc/serial.h>
+#include <isc/stdtime.h>
+#include <isc/msgcat.h>
+
+#include <dns/db.h>
+#include <dns/rdatalist.h>
+#include <dns/rdataset.h>
+#include <dns/rdatasetiter.h>
+#include <dns/nsec3.h>
+
+#include "dnstest.h"
+
+static char tempname[11] = "dtXXXXXXXX";
+
+static void
+local_callback(const char *file, int line, isc_assertiontype_t type,
+ const char *cond)
+{
+ UNUSED(file); UNUSED(line); UNUSED(type); UNUSED(cond);
+ if (strcmp(tempname, "dtXXXXXXXX"))
+ unlink(tempname);
+ atf_tc_pass();
+ exit(0);
+}
+
+static dns_db_t *db1 = NULL, *db2 = NULL;
+static dns_dbversion_t *v1 = NULL, *v2 = NULL;
+
+static void
+setup_db(void) {
+ isc_result_t result;
+ result = dns_db_create(mctx, "rbt", dns_rootname, dns_dbtype_zone,
+ dns_rdataclass_in, 0, NULL, &db1);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ dns_db_newversion(db1, &v1);
+
+ result = dns_db_create(mctx, "rbt", dns_rootname, dns_dbtype_zone,
+ dns_rdataclass_in, 0, NULL, &db2);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ dns_db_newversion(db2, &v2);
+}
+
+static void
+close_db(void) {
+ if (v1 != NULL) {
+ dns_db_closeversion(db1, &v1, false);
+ ATF_REQUIRE_EQ(v1, NULL);
+ }
+ if (db1 != NULL) {
+ dns_db_detach(&db1);
+ ATF_REQUIRE_EQ(db1, NULL);
+ }
+
+ if (v2 != NULL) {
+ dns_db_closeversion(db2, &v2, false);
+ ATF_REQUIRE_EQ(v2, NULL);
+ }
+ if (db2 != NULL) {
+ dns_db_detach(&db2);
+ ATF_REQUIRE_EQ(db2, NULL);
+ }
+}
+
+#define VERSION(callback) ((callback == NULL) ? v1 : v2)
+#define VERSIONP(callback) ((callback == NULL) ? &v1 : &v2)
+/*
+ * Individual unit tests
+ */
+static void
+attachversion(isc_assertioncallback_t callback) {
+ isc_result_t result;
+ dns_dbversion_t *v = NULL;
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ setup_db();
+
+ isc_assertion_setcallback(callback);
+ dns_db_attachversion(db1, VERSION(callback), &v);
+ if (callback != NULL)
+ atf_tc_fail("dns_db_attachversion did not assert");
+
+ ATF_REQUIRE_EQ(v, v1);
+ dns_db_closeversion(db1, &v, false);
+ ATF_REQUIRE_EQ(v, NULL);
+
+ close_db();
+ dns_test_end();
+}
+
+ATF_TC(attachversion);
+ATF_TC_HEAD(attachversion, tc) {
+ atf_tc_set_md_var(tc, "descr", "check dns_db_attachversion passes with matching db/verison");
+}
+ATF_TC_BODY(attachversion, tc) {
+
+ UNUSED(tc);
+
+ attachversion(NULL);
+}
+
+ATF_TC(attachversion_bad);
+ATF_TC_HEAD(attachversion_bad, tc) {
+ atf_tc_set_md_var(tc, "descr", "check dns_db_attachversion aborts with mis-matching db/verison");
+}
+ATF_TC_BODY(attachversion_bad, tc) {
+
+ UNUSED(tc);
+
+ attachversion(local_callback);
+}
+
+static void
+closeversion(isc_assertioncallback_t callback) {
+ isc_result_t result;
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ setup_db();
+
+ isc_assertion_setcallback(callback);
+ dns_db_closeversion(db1, VERSIONP(callback), false);
+ if (callback != NULL)
+ atf_tc_fail("dns_db_closeversion did not assert");
+ ATF_REQUIRE_EQ(v1, NULL);
+
+ close_db();
+ dns_test_end();
+}
+
+ATF_TC(closeversion);
+ATF_TC_HEAD(closeversion, tc) {
+ atf_tc_set_md_var(tc, "descr", "check dns_db_closeversion passes with matching db/verison");
+}
+ATF_TC_BODY(closeversion, tc) {
+
+ UNUSED(tc);
+
+ closeversion(NULL);
+}
+
+ATF_TC(closeversion_bad);
+ATF_TC_HEAD(closeversion_bad, tc) {
+ atf_tc_set_md_var(tc, "descr", "check dns_db_closeversion asserts with mis-matching db/verison");
+}
+ATF_TC_BODY(closeversion_bad, tc) {
+
+ UNUSED(tc);
+
+ closeversion(local_callback);
+}
+
+static void
+find(isc_assertioncallback_t callback) {
+ isc_result_t result;
+ dns_rdataset_t rdataset;
+ dns_fixedname_t fixed;
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ setup_db();
+
+ dns_rdataset_init(&rdataset);
+ dns_fixedname_init(&fixed);
+
+ isc_assertion_setcallback(callback);
+ result = dns_db_find(db1, dns_rootname, VERSION(callback),
+ dns_rdatatype_soa, 0, 0, NULL,
+ dns_fixedname_name(&fixed), &rdataset, NULL);
+ if (callback != NULL)
+ atf_tc_fail("dns_db_find did not assert");
+ ATF_REQUIRE_EQ(result, DNS_R_NXDOMAIN);
+
+ close_db();
+
+ dns_test_end();
+}
+ATF_TC(find);
+ATF_TC_HEAD(find, tc) {
+ atf_tc_set_md_var(tc, "descr", "check dns_db_find passes with matching db/version");
+}
+ATF_TC_BODY(find, tc) {
+
+ UNUSED(tc);
+
+ find(NULL);
+}
+
+ATF_TC(find_bad);
+ATF_TC_HEAD(find_bad, tc) {
+ atf_tc_set_md_var(tc, "descr", "check dns_db_find asserts with mis-matching db/version");
+}
+ATF_TC_BODY(find_bad, tc) {
+
+ UNUSED(tc);
+
+ find(local_callback);
+}
+
+static void
+allrdatasets(isc_assertioncallback_t callback) {
+ isc_result_t result;
+ dns_dbnode_t *node = NULL;
+ dns_rdatasetiter_t *iterator = NULL;
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ setup_db();
+
+ result = dns_db_findnode(db1, dns_rootname, false, &node);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ isc_assertion_setcallback(callback);
+ result = dns_db_allrdatasets(db1, node, VERSION(callback), 0,
+ &iterator);
+ if (callback != NULL)
+ atf_tc_fail("dns_db_allrdatasets did not assert");
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ dns_rdatasetiter_destroy(&iterator);
+ ATF_REQUIRE_EQ(iterator, NULL);
+
+ dns_db_detachnode(db1, &node);
+ ATF_REQUIRE_EQ(node, NULL);
+
+ close_db();
+
+ dns_test_end();
+}
+
+ATF_TC(allrdatasets);
+ATF_TC_HEAD(allrdatasets, tc) {
+ atf_tc_set_md_var(tc, "descr", "check dns_db_allrdatasets passes with matching db/version");
+}
+ATF_TC_BODY(allrdatasets, tc) {
+
+ UNUSED(tc);
+
+ allrdatasets(NULL);
+}
+
+ATF_TC(allrdatasets_bad);
+ATF_TC_HEAD(allrdatasets_bad, tc) {
+ atf_tc_set_md_var(tc, "descr", "check dns_db_allrdatasets aborts with mis-matching db/version");
+}
+ATF_TC_BODY(allrdatasets_bad, tc) {
+
+ UNUSED(tc);
+
+ allrdatasets(local_callback);
+}
+
+static void
+findrdataset(isc_assertioncallback_t callback) {
+ isc_result_t result;
+ dns_rdataset_t rdataset;
+ dns_fixedname_t fixed;
+ dns_dbnode_t *node = NULL;
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ setup_db();
+
+ dns_rdataset_init(&rdataset);
+ dns_fixedname_init(&fixed);
+
+ result = dns_db_findnode(db1, dns_rootname, false, &node);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ isc_assertion_setcallback(callback);
+ result = dns_db_findrdataset(db1, node, VERSION(callback),
+ dns_rdatatype_soa, 0, 0, &rdataset, NULL);
+ if (callback != NULL)
+ atf_tc_fail("dns_db_findrdataset did not assert");
+ ATF_REQUIRE_EQ(result, ISC_R_NOTFOUND);
+
+ dns_db_detachnode(db1, &node);
+ ATF_REQUIRE_EQ(node, NULL);
+
+ close_db();
+
+ dns_test_end();
+}
+
+ATF_TC(findrdataset);
+ATF_TC_HEAD(findrdataset, tc) {
+ atf_tc_set_md_var(tc, "descr", "check dns_db_findrdataset passes with matching db/version");
+}
+ATF_TC_BODY(findrdataset, tc) {
+
+ UNUSED(tc);
+
+ findrdataset(NULL);
+}
+
+ATF_TC(findrdataset_bad);
+ATF_TC_HEAD(findrdataset_bad, tc) {
+ atf_tc_set_md_var(tc, "descr", "check dns_db_findrdataset aborts with mis-matching db/version");
+}
+ATF_TC_BODY(findrdataset_bad, tc) {
+
+ UNUSED(tc);
+
+ findrdataset(local_callback);
+}
+
+static void
+deleterdataset(isc_assertioncallback_t callback) {
+ isc_result_t result;
+ dns_rdataset_t rdataset;
+ dns_fixedname_t fixed;
+ dns_dbnode_t *node = NULL;
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ setup_db();
+
+ dns_rdataset_init(&rdataset);
+ dns_fixedname_init(&fixed);
+
+ result = dns_db_findnode(db1, dns_rootname, false, &node);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ isc_assertion_setcallback(callback);
+ result = dns_db_deleterdataset(db1, node, VERSION(callback),
+ dns_rdatatype_soa, 0);
+ if (callback != NULL)
+ atf_tc_fail("dns_db_deleterdataset did not assert");
+ ATF_REQUIRE_EQ(result, DNS_R_UNCHANGED);
+
+ dns_db_detachnode(db1, &node);
+ ATF_REQUIRE_EQ(node, NULL);
+
+ close_db();
+
+ dns_test_end();
+}
+
+ATF_TC(deleterdataset);
+ATF_TC_HEAD(deleterdataset, tc) {
+ atf_tc_set_md_var(tc, "descr", "check dns_db_deleterdataset passes with matching db/version");
+}
+ATF_TC_BODY(deleterdataset, tc) {
+
+ UNUSED(tc);
+
+ deleterdataset(NULL);
+}
+
+ATF_TC(deleterdataset_bad);
+ATF_TC_HEAD(deleterdataset_bad, tc) {
+ atf_tc_set_md_var(tc, "descr", "check dns_db_deleterdataset aborts with mis-matching db/version");
+}
+ATF_TC_BODY(deleterdataset_bad, tc) {
+
+ UNUSED(tc);
+
+ deleterdataset(local_callback);
+}
+
+static void
+subtract(isc_assertioncallback_t callback) {
+ isc_result_t result;
+ dns_rdataset_t rdataset;
+ dns_fixedname_t fixed;
+ dns_dbnode_t *node = NULL;
+ dns_rdatalist_t rdatalist;
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ setup_db();
+
+ dns_rdataset_init(&rdataset);
+ dns_rdatalist_init(&rdatalist);
+ dns_fixedname_init(&fixed);
+
+ rdatalist.rdclass = dns_rdataclass_in;
+
+ result = dns_rdatalist_tordataset(&rdatalist, &rdataset);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_db_findnode(db1, dns_rootname, false, &node);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ isc_assertion_setcallback(callback);
+ result = dns_db_subtractrdataset(db1, node, VERSION(callback),
+ &rdataset, 0, NULL);
+ if (callback != NULL)
+ atf_tc_fail("dns_db_subtractrdataset did not assert");
+ ATF_REQUIRE_EQ(result, DNS_R_UNCHANGED);
+
+ dns_db_detachnode(db1, &node);
+ ATF_REQUIRE_EQ(node, NULL);
+
+ close_db();
+
+ dns_test_end();
+}
+
+ATF_TC(subtractrdataset);
+ATF_TC_HEAD(subtractrdataset, tc) {
+ atf_tc_set_md_var(tc, "descr", "check dns_db_subtractrdataset passes with matching db/version");
+}
+ATF_TC_BODY(subtractrdataset, tc) {
+
+ UNUSED(tc);
+
+ subtract(NULL);
+}
+
+ATF_TC(subtractrdataset_bad);
+ATF_TC_HEAD(subtractrdataset_bad, tc) {
+ atf_tc_set_md_var(tc, "descr", "check dns_db_subtractrdataset aborts with mis-matching db/version");
+}
+ATF_TC_BODY(subtractrdataset_bad, tc) {
+
+ UNUSED(tc);
+
+ subtract(local_callback);
+}
+
+static void
+dump(isc_assertioncallback_t callback) {
+ isc_result_t result;
+ FILE *f = NULL;
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ setup_db();
+
+ result = isc_file_openunique(tempname, &f);
+ fclose(f);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ isc_assertion_setcallback(callback);
+ result = dns_db_dump(db1, VERSION(callback), tempname);
+ (void)unlink(tempname);
+ if (callback != NULL)
+ atf_tc_fail("dns_db_dump did not assert");
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ close_db();
+
+ dns_test_end();
+}
+
+ATF_TC(dump);
+ATF_TC_HEAD(dump, tc) {
+ atf_tc_set_md_var(tc, "descr", "check dns_db_dump passes with matching db/version");
+}
+ATF_TC_BODY(dump, tc) {
+
+ UNUSED(tc);
+
+ dump(NULL);
+}
+
+ATF_TC(dump_bad);
+ATF_TC_HEAD(dump_bad, tc) {
+ atf_tc_set_md_var(tc, "descr", "check dns_db_dump aborts with mis-matching db/version");
+}
+ATF_TC_BODY(dump_bad, tc) {
+
+ UNUSED(tc);
+
+ dump(local_callback);
+}
+
+static void
+addrdataset(isc_assertioncallback_t callback) {
+ isc_result_t result;
+ dns_rdataset_t rdataset;
+ dns_fixedname_t fixed;
+ dns_dbnode_t *node = NULL;
+ dns_rdatalist_t rdatalist;
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ setup_db();
+
+ dns_rdataset_init(&rdataset);
+ dns_rdatalist_init(&rdatalist);
+ dns_fixedname_init(&fixed);
+
+ rdatalist.rdclass = dns_rdataclass_in;
+
+ result = dns_rdatalist_tordataset(&rdatalist, &rdataset);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_db_findnode(db1, dns_rootname, false, &node);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ isc_assertion_setcallback(callback);
+ result = dns_db_addrdataset(db1, node, VERSION(callback), 0, &rdataset,
+ 0, NULL);
+ if (callback != NULL)
+ atf_tc_fail("dns_db_adddataset did not assert");
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ dns_db_detachnode(db1, &node);
+ ATF_REQUIRE_EQ(node, NULL);
+
+ close_db();
+
+ dns_test_end();
+}
+
+ATF_TC(addrdataset);
+ATF_TC_HEAD(addrdataset, tc) {
+ atf_tc_set_md_var(tc, "descr", "check dns_db_addrdataset passes with matching db/version");
+}
+ATF_TC_BODY(addrdataset, tc) {
+
+ UNUSED(tc);
+
+ addrdataset(NULL);
+}
+
+ATF_TC(addrdataset_bad);
+ATF_TC_HEAD(addrdataset_bad, tc) {
+ atf_tc_set_md_var(tc, "descr", "check dns_db_addrdataset aborts with mis-matching db/version");
+}
+ATF_TC_BODY(addrdataset_bad, tc) {
+
+ UNUSED(tc);
+
+ addrdataset(local_callback);
+}
+
+static void
+getnsec3parameters(isc_assertioncallback_t callback) {
+ isc_result_t result;
+ dns_hash_t hash;
+ uint8_t flags;
+ uint16_t iterations;
+ unsigned char salt[DNS_NSEC3_SALTSIZE];
+ size_t salt_length = sizeof(salt);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ setup_db();
+
+ isc_assertion_setcallback(callback);
+ result = dns_db_getnsec3parameters(db1, VERSION(callback), &hash,
+ &flags, &iterations, salt,
+ &salt_length);
+ if (callback != NULL)
+ atf_tc_fail("dns_db_dump did not assert");
+ ATF_REQUIRE_EQ(result, ISC_R_NOTFOUND);
+
+ close_db();
+
+ dns_test_end();
+}
+
+ATF_TC(getnsec3parameters);
+ATF_TC_HEAD(getnsec3parameters, tc) {
+ atf_tc_set_md_var(tc, "descr", "check dns_db_getnsec3parameters passes with matching db/version");
+}
+ATF_TC_BODY(getnsec3parameters, tc) {
+
+ UNUSED(tc);
+
+ getnsec3parameters(NULL);
+}
+
+ATF_TC(getnsec3parameters_bad);
+ATF_TC_HEAD(getnsec3parameters_bad, tc) {
+ atf_tc_set_md_var(tc, "descr", "check dns_db_getnsec3parameters aborts with mis-matching db/version");
+}
+ATF_TC_BODY(getnsec3parameters_bad, tc) {
+
+ UNUSED(tc);
+
+ getnsec3parameters(local_callback);
+}
+
+static void
+resigned(isc_assertioncallback_t callback) {
+ isc_result_t result;
+ dns_rdataset_t rdataset, added;
+ dns_dbnode_t *node = NULL;
+ dns_rdatalist_t rdatalist;
+ dns_rdata_rrsig_t rrsig;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_buffer_t b;
+ unsigned char buf[1024];
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ setup_db();
+
+ /*
+ * Create a dummy RRSIG record and set a resigning time.
+ */
+ dns_rdataset_init(&added);
+ dns_rdataset_init(&rdataset);
+ dns_rdatalist_init(&rdatalist);
+ isc_buffer_init(&b, buf, sizeof(buf));
+
+ DNS_RDATACOMMON_INIT(&rrsig, dns_rdatatype_rrsig, dns_rdataclass_in);
+ rrsig.covered = dns_rdatatype_a;
+ rrsig.algorithm = 100;
+ rrsig.labels = 0;
+ rrsig.originalttl = 0;
+ rrsig.timeexpire = 3600;
+ rrsig.timesigned = 0;
+ rrsig.keyid = 0;
+ dns_name_init(&rrsig.signer, NULL);
+ dns_name_clone(dns_rootname, &rrsig.signer);
+ rrsig.siglen = 0;
+ rrsig.signature = NULL;
+
+ result = dns_rdata_fromstruct(&rdata, dns_rdataclass_in,
+ dns_rdatatype_rrsig, &rrsig, &b);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ rdatalist.rdclass = dns_rdataclass_in;
+ rdatalist.type = dns_rdatatype_rrsig;
+ ISC_LIST_APPEND(rdatalist.rdata, &rdata, link);
+
+ result = dns_rdatalist_tordataset(&rdatalist, &rdataset);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ rdataset.attributes |= DNS_RDATASETATTR_RESIGN;
+ rdataset.resign = 7200;
+
+ result = dns_db_findnode(db1, dns_rootname, false, &node);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_db_addrdataset(db1, node, v1, 0, &rdataset, 0, &added);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ dns_db_detachnode(db1, &node);
+ ATF_REQUIRE_EQ(node, NULL);
+
+ isc_assertion_setcallback(callback);
+ dns_db_resigned(db1, &added, VERSION(callback));
+ if (callback != NULL)
+ atf_tc_fail("dns_db_resigned did not assert");
+
+ dns_rdataset_disassociate(&added);
+
+ close_db();
+
+ dns_test_end();
+}
+
+ATF_TC(resigned);
+ATF_TC_HEAD(resigned, tc) {
+ atf_tc_set_md_var(tc, "descr", "check dns_rdataset_resigned passes with matching db/version");
+}
+ATF_TC_BODY(resigned, tc) {
+
+ UNUSED(tc);
+
+ resigned(NULL);
+}
+
+ATF_TC(resigned_bad);
+ATF_TC_HEAD(resigned_bad, tc) {
+ atf_tc_set_md_var(tc, "descr", "check dns_rdataset_resigned aborts with mis-matching db/version");
+}
+ATF_TC_BODY(resigned_bad, tc) {
+
+ UNUSED(tc);
+
+ resigned(local_callback);
+}
+
+/*
+ * Main
+ */
+ATF_TP_ADD_TCS(tp) {
+ ATF_TP_ADD_TC(tp, dump);
+ ATF_TP_ADD_TC(tp, dump_bad);
+ ATF_TP_ADD_TC(tp, find);
+ ATF_TP_ADD_TC(tp, find_bad);
+ ATF_TP_ADD_TC(tp, allrdatasets);
+ ATF_TP_ADD_TC(tp, allrdatasets_bad);
+ ATF_TP_ADD_TC(tp, findrdataset);
+ ATF_TP_ADD_TC(tp, findrdataset_bad);
+ ATF_TP_ADD_TC(tp, addrdataset);
+ ATF_TP_ADD_TC(tp, addrdataset_bad);
+ ATF_TP_ADD_TC(tp, deleterdataset);
+ ATF_TP_ADD_TC(tp, deleterdataset_bad);
+ ATF_TP_ADD_TC(tp, subtractrdataset);
+ ATF_TP_ADD_TC(tp, subtractrdataset_bad);
+ ATF_TP_ADD_TC(tp, attachversion);
+ ATF_TP_ADD_TC(tp, attachversion_bad);
+ ATF_TP_ADD_TC(tp, closeversion);
+ ATF_TP_ADD_TC(tp, closeversion_bad);
+ ATF_TP_ADD_TC(tp, getnsec3parameters);
+ ATF_TP_ADD_TC(tp, getnsec3parameters_bad);
+ ATF_TP_ADD_TC(tp, resigned);
+ ATF_TP_ADD_TC(tp, resigned_bad);
+
+ return (atf_no_error());
+}
diff --git a/lib/dns/tests/dh_test.c b/lib/dns/tests/dh_test.c
new file mode 100644
index 0000000..6216b4e
--- /dev/null
+++ b/lib/dns/tests/dh_test.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/* ! \file */
+
+#include <config.h>
+
+#include <atf-c.h>
+
+#include <unistd.h>
+
+#include <isc/util.h>
+#include <isc/string.h>
+
+#include <pk11/site.h>
+
+#include <dns/name.h>
+#include <dst/result.h>
+
+#include "../dst_internal.h"
+
+#include "dnstest.h"
+
+#if defined(OPENSSL) && !defined(PK11_DH_DISABLE)
+
+ATF_TC(isc_dh_computesecret);
+ATF_TC_HEAD(isc_dh_computesecret, tc) {
+ atf_tc_set_md_var(tc, "descr", "OpenSSL DH_compute_key() failure");
+}
+ATF_TC_BODY(isc_dh_computesecret, tc) {
+ dst_key_t *key = NULL;
+ isc_buffer_t buf;
+ unsigned char array[1024];
+ isc_result_t ret;
+ dns_fixedname_t fname;
+ dns_name_t *name;
+
+ UNUSED(tc);
+
+ ret = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(ret, ISC_R_SUCCESS);
+
+ name = dns_fixedname_initname(&fname);
+ isc_buffer_constinit(&buf, "dh.", 3);
+ isc_buffer_add(&buf, 3);
+ ret = dns_name_fromtext(name, &buf, NULL, 0, NULL);
+ ATF_REQUIRE_EQ(ret, ISC_R_SUCCESS);
+
+ ret = dst_key_fromfile(name, 18602, DST_ALG_DH,
+ DST_TYPE_PUBLIC | DST_TYPE_KEY,
+ "./", mctx, &key);
+ ATF_REQUIRE_EQ(ret, ISC_R_SUCCESS);
+
+ isc_buffer_init(&buf, array, sizeof(array));
+ ret = dst_key_computesecret(key, key, &buf);
+ ATF_REQUIRE_EQ(ret, DST_R_NOTPRIVATEKEY);
+ ret = key->func->computesecret(key, key, &buf);
+ ATF_REQUIRE_EQ(ret, DST_R_COMPUTESECRETFAILURE);
+
+ dst_key_free(&key);
+ dns_test_end();
+}
+#else
+ATF_TC(untested);
+ATF_TC_HEAD(untested, tc) {
+ atf_tc_set_md_var(tc, "descr", "skipping OpenSSL DH test");
+}
+ATF_TC_BODY(untested, tc) {
+ UNUSED(tc);
+ atf_tc_skip("OpenSSL DH not compiled in");
+}
+#endif
+/*
+ * Main
+ */
+ATF_TP_ADD_TCS(tp) {
+#if defined(OPENSSL) && !defined(PK11_DH_DISABLE)
+ ATF_TP_ADD_TC(tp, isc_dh_computesecret);
+#else
+ ATF_TP_ADD_TC(tp, untested);
+#endif
+ return (atf_no_error());
+}
diff --git a/lib/dns/tests/dispatch_test.c b/lib/dns/tests/dispatch_test.c
new file mode 100644
index 0000000..156fe9f
--- /dev/null
+++ b/lib/dns/tests/dispatch_test.c
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#include <isc/app.h>
+#include <isc/buffer.h>
+#include <isc/socket.h>
+#include <isc/task.h>
+#include <isc/timer.h>
+
+#include <dns/dispatch.h>
+#include <dns/name.h>
+#include <dns/view.h>
+
+#include "dnstest.h"
+
+dns_dispatchmgr_t *dispatchmgr = NULL;
+dns_dispatchset_t *dset = NULL;
+
+static isc_result_t
+make_dispatchset(unsigned int ndisps) {
+ isc_result_t result;
+ isc_sockaddr_t any;
+ unsigned int attrs;
+ dns_dispatch_t *disp = NULL;
+
+ result = dns_dispatchmgr_create(mctx, NULL, &dispatchmgr);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ isc_sockaddr_any(&any);
+ attrs = DNS_DISPATCHATTR_IPV4 | DNS_DISPATCHATTR_UDP;
+ result = dns_dispatch_getudp(dispatchmgr, socketmgr, taskmgr,
+ &any, 512, 6, 1024, 17, 19, attrs,
+ attrs, &disp);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = dns_dispatchset_create(mctx, socketmgr, taskmgr, disp,
+ &dset, ndisps);
+ dns_dispatch_detach(&disp);
+
+ return (result);
+}
+
+static void
+teardown(void) {
+ if (dset != NULL)
+ dns_dispatchset_destroy(&dset);
+ if (dispatchmgr != NULL)
+ dns_dispatchmgr_destroy(&dispatchmgr);
+}
+
+/*
+ * Individual unit tests
+ */
+ATF_TC(dispatchset_create);
+ATF_TC_HEAD(dispatchset_create, tc) {
+ atf_tc_set_md_var(tc, "descr", "create dispatch set");
+}
+ATF_TC_BODY(dispatchset_create, tc) {
+ isc_result_t result;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, true);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = make_dispatchset(1);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ teardown();
+
+ result = make_dispatchset(10);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ teardown();
+
+ dns_test_end();
+}
+
+ATF_TC(dispatchset_get);
+ATF_TC_HEAD(dispatchset_get, tc) {
+ atf_tc_set_md_var(tc, "descr", "test dispatch set round-robin");
+}
+ATF_TC_BODY(dispatchset_get, tc) {
+ isc_result_t result;
+ dns_dispatch_t *d1, *d2, *d3, *d4, *d5;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, true);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = make_dispatchset(1);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ d1 = dns_dispatchset_get(dset);
+ d2 = dns_dispatchset_get(dset);
+ d3 = dns_dispatchset_get(dset);
+ d4 = dns_dispatchset_get(dset);
+ d5 = dns_dispatchset_get(dset);
+
+ ATF_CHECK_EQ(d1, d2);
+ ATF_CHECK_EQ(d2, d3);
+ ATF_CHECK_EQ(d3, d4);
+ ATF_CHECK_EQ(d4, d5);
+
+ teardown();
+
+ result = make_dispatchset(4);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ d1 = dns_dispatchset_get(dset);
+ d2 = dns_dispatchset_get(dset);
+ d3 = dns_dispatchset_get(dset);
+ d4 = dns_dispatchset_get(dset);
+ d5 = dns_dispatchset_get(dset);
+
+ ATF_CHECK_EQ(d1, d5);
+ ATF_CHECK(d1 != d2);
+ ATF_CHECK(d2 != d3);
+ ATF_CHECK(d3 != d4);
+ ATF_CHECK(d4 != d5);
+
+ teardown();
+ dns_test_end();
+}
+
+static void
+senddone(isc_task_t *task, isc_event_t *event) {
+ isc_socket_t *sock = event->ev_arg;
+
+ UNUSED(task);
+
+ isc_socket_detach(&sock);
+ isc_event_free(&event);
+}
+
+static void
+nameserver(isc_task_t *task, isc_event_t *event) {
+ isc_result_t result;
+ isc_region_t region;
+ isc_socket_t *dummy;
+ isc_socket_t *sock = event->ev_arg;
+ isc_socketevent_t *ev = (isc_socketevent_t *)event;
+ static unsigned char buf1[16];
+ static unsigned char buf2[16];
+
+ memmove(buf1, ev->region.base, 12);
+ memset(buf1 + 12, 0, 4);
+ buf1[2] |= 0x80; /* qr=1 */
+
+ memmove(buf2, ev->region.base, 12);
+ memset(buf2 + 12, 1, 4);
+ buf2[2] |= 0x80; /* qr=1 */
+
+ /*
+ * send message to be discarded.
+ */
+ region.base = buf1;
+ region.length = sizeof(buf1);
+ dummy = NULL;
+ isc_socket_attach(sock, &dummy);
+ result = isc_socket_sendto(sock, &region, task, senddone, sock,
+ &ev->address, NULL);
+ if (result != ISC_R_SUCCESS)
+ isc_socket_detach(&dummy);
+
+ /*
+ * send nextitem message.
+ */
+ region.base = buf2;
+ region.length = sizeof(buf2);
+ dummy = NULL;
+ isc_socket_attach(sock, &dummy);
+ result = isc_socket_sendto(sock, &region, task, senddone, sock,
+ &ev->address, NULL);
+ if (result != ISC_R_SUCCESS)
+ isc_socket_detach(&dummy);
+ isc_event_free(&event);
+}
+
+static dns_dispatch_t *dispatch = NULL;
+static dns_dispentry_t *dispentry = NULL;
+static bool first = true;
+static isc_mutex_t lock;
+static isc_sockaddr_t local;
+static unsigned int responses = 0;
+
+static void
+response(isc_task_t *task, isc_event_t *event) {
+ dns_dispatchevent_t *devent = (dns_dispatchevent_t *)event;
+ isc_result_t result;
+ bool wasfirst;
+
+ UNUSED(task);
+
+ LOCK(&lock);
+ wasfirst = first;
+ first = false;
+ responses++;
+ UNLOCK(&lock);
+
+ if (wasfirst) {
+ result = dns_dispatch_getnext(dispentry, &devent);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ } else {
+ dns_dispatch_removeresponse(&dispentry, &devent);
+ isc_app_shutdown();
+ }
+}
+
+static void
+startit(isc_task_t *task, isc_event_t *event) {
+ isc_result_t result;
+ isc_socket_t *sock = NULL;
+
+ isc_socket_attach(dns_dispatch_getsocket(dispatch), &sock);
+ result = isc_socket_sendto(sock, event->ev_arg, task, senddone, sock,
+ &local, NULL);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ isc_event_free(&event);
+}
+
+ATF_TC(dispatch_getnext);
+ATF_TC_HEAD(dispatch_getnext, tc) {
+ atf_tc_set_md_var(tc, "descr", "test dispatch getnext");
+}
+ATF_TC_BODY(dispatch_getnext, tc) {
+ isc_region_t region;
+ isc_result_t result;
+ isc_socket_t *sock = NULL;
+ isc_task_t *task = NULL;
+ uint16_t id;
+ struct in_addr ina;
+ unsigned char message[12];
+ unsigned int attrs;
+ unsigned char rbuf[12];
+
+ UNUSED(tc);
+
+ result = isc_mutex_init(&lock);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_test_begin(NULL, true);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = isc_task_create(taskmgr, 0, &task);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_dispatchmgr_create(mctx, NULL, &dispatchmgr);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ ina.s_addr = htonl(INADDR_LOOPBACK);
+ isc_sockaddr_fromin(&local, &ina, 0);
+ attrs = DNS_DISPATCHATTR_IPV4 | DNS_DISPATCHATTR_UDP;
+ result = dns_dispatch_getudp(dispatchmgr, socketmgr, taskmgr,
+ &local, 512, 6, 1024, 17, 19, attrs,
+ attrs, &dispatch);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ /*
+ * Create a local udp nameserver on the loopback.
+ */
+ result = isc_socket_create(socketmgr, AF_INET, isc_sockettype_udp,
+ &sock);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ ina.s_addr = htonl(INADDR_LOOPBACK);
+ isc_sockaddr_fromin(&local, &ina, 0);
+ result = isc_socket_bind(sock, &local, 0);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = isc_socket_getsockname(sock, &local);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ first = true;
+ region.base = rbuf;
+ region.length = sizeof(rbuf);
+ result = isc_socket_recv(sock, &region, 1, task, nameserver, sock);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_dispatch_addresponse(dispatch, &local, task, response,
+ NULL, &id, &dispentry);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ memset(message, 0, sizeof(message));
+ message[0] = (id >> 8) & 0xff;
+ message[1] = id & 0xff;
+
+ region.base = message;
+ region.length = sizeof(message);
+ result = isc_app_onrun(mctx, task, startit, &region);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = isc_app_run();
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ ATF_CHECK_EQ(responses, 2);
+
+ /*
+ * Shutdown nameserver.
+ */
+ isc_socket_cancel(sock, task, ISC_SOCKCANCEL_RECV);
+ isc_socket_detach(&sock);
+ isc_task_detach(&task);
+
+ /*
+ * Shutdown the dispatch.
+ */
+ dns_dispatch_detach(&dispatch);
+ dns_dispatchmgr_destroy(&dispatchmgr);
+
+ dns_test_end();
+}
+
+/*
+ * Main
+ */
+ATF_TP_ADD_TCS(tp) {
+ ATF_TP_ADD_TC(tp, dispatchset_create);
+ ATF_TP_ADD_TC(tp, dispatchset_get);
+ ATF_TP_ADD_TC(tp, dispatch_getnext);
+ return (atf_no_error());
+}
diff --git a/lib/dns/tests/dnstap_test.c b/lib/dns/tests/dnstap_test.c
new file mode 100644
index 0000000..56e3da4
--- /dev/null
+++ b/lib/dns/tests/dnstap_test.c
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <atf-c.h>
+
+#include <unistd.h>
+#include <inttypes.h>
+#include <stdlib.h>
+
+#include <isc/buffer.h>
+#include <isc/file.h>
+#include <isc/stdio.h>
+#include <isc/print.h>
+#include <isc/types.h>
+
+#include <dns/dnstap.h>
+#include <dns/view.h>
+
+#include "dnstest.h"
+
+#ifdef HAVE_DNSTAP
+#include <dns/dnstap.pb-c.h>
+#include <protobuf-c/protobuf-c.h>
+
+#define TAPFILE "testdata/dnstap/dnstap.file"
+#define TAPSOCK "testdata/dnstap/dnstap.sock"
+
+#define TAPSAVED "testdata/dnstap/dnstap.saved"
+#define TAPTEXT "testdata/dnstap/dnstap.text"
+
+/*
+ * Helper functions
+ */
+static void
+cleanup() {
+ (void) isc_file_remove(TAPFILE);
+ (void) isc_file_remove(TAPSOCK);
+}
+
+/*
+ * Individual unit tests
+ */
+
+ATF_TC(create);
+ATF_TC_HEAD(create, tc) {
+ atf_tc_set_md_var(tc, "descr", "set up dnstap environment");
+}
+ATF_TC_BODY(create, tc) {
+ isc_result_t result;
+ dns_dtenv_t *dtenv = NULL;
+ struct fstrm_iothr_options *fopt;
+
+ UNUSED(tc);
+
+ cleanup();
+
+ result = dns_test_begin(NULL, true);
+ ATF_REQUIRE(result == ISC_R_SUCCESS);
+
+ fopt = fstrm_iothr_options_init();
+ ATF_REQUIRE(fopt != NULL);
+ fstrm_iothr_options_set_num_input_queues(fopt, 1);
+
+ result = dns_dt_create(mctx, dns_dtmode_file, TAPFILE, &fopt, &dtenv);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ if (dtenv != NULL)
+ dns_dt_detach(&dtenv);
+ if (fopt != NULL)
+ fstrm_iothr_options_destroy(&fopt);
+
+ ATF_CHECK(isc_file_exists(TAPFILE));
+
+ fopt = fstrm_iothr_options_init();
+ ATF_REQUIRE(fopt != NULL);
+ fstrm_iothr_options_set_num_input_queues(fopt, 1);
+
+ result = dns_dt_create(mctx, dns_dtmode_unix, TAPSOCK, &fopt, &dtenv);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ if (dtenv != NULL)
+ dns_dt_detach(&dtenv);
+ if (fopt != NULL)
+ fstrm_iothr_options_destroy(&fopt);
+
+ /* 'create' should succeed, but the file shouldn't exist yet */
+ ATF_CHECK(!isc_file_exists(TAPSOCK));
+
+ fopt = fstrm_iothr_options_init();
+ ATF_REQUIRE(fopt != NULL);
+ fstrm_iothr_options_set_num_input_queues(fopt, 1);
+
+ result = dns_dt_create(mctx, 33, TAPSOCK, &fopt, &dtenv);
+ ATF_CHECK_EQ(result, ISC_R_FAILURE);
+ ATF_CHECK_EQ(dtenv, NULL);
+ if (dtenv != NULL)
+ dns_dt_detach(&dtenv);
+ if (fopt != NULL)
+ fstrm_iothr_options_destroy(&fopt);
+
+ cleanup();
+
+ dns_dt_shutdown();
+ dns_test_end();
+}
+
+ATF_TC(send);
+ATF_TC_HEAD(send, tc) {
+ atf_tc_set_md_var(tc, "descr", "send dnstap messages");
+}
+ATF_TC_BODY(send, tc) {
+ isc_result_t result;
+ dns_dtenv_t *dtenv = NULL;
+ dns_dthandle_t *handle = NULL;
+ uint8_t *data;
+ size_t dsize;
+ unsigned char zone[DNS_NAME_MAXWIRE];
+ unsigned char qambuffer[4096], rambuffer[4096];
+ unsigned char qrmbuffer[4096], rrmbuffer[4096];
+ isc_buffer_t zb, qamsg, ramsg, qrmsg, rrmsg;
+ size_t qasize, qrsize, rasize, rrsize;
+ dns_fixedname_t zfname;
+ dns_name_t *zname;
+ dns_dtmsgtype_t dt;
+ dns_view_t *view = NULL;
+ dns_compress_t cctx;
+ isc_region_t zr;
+ isc_sockaddr_t qaddr;
+ isc_sockaddr_t raddr;
+ struct in_addr in;
+ isc_stdtime_t now;
+ isc_time_t p, f;
+ struct fstrm_iothr_options *fopt;
+
+ UNUSED(tc);
+
+ cleanup();
+
+ result = dns_test_begin(NULL, true);
+ ATF_REQUIRE(result == ISC_R_SUCCESS);
+
+ result = dns_test_makeview("test", &view);
+
+ fopt = fstrm_iothr_options_init();
+ ATF_REQUIRE(fopt != NULL);
+ fstrm_iothr_options_set_num_input_queues(fopt, 1);
+
+ result = dns_dt_create(mctx, dns_dtmode_file, TAPFILE, &fopt, &dtenv);
+ ATF_REQUIRE(result == ISC_R_SUCCESS);
+
+ dns_dt_attach(dtenv, &view->dtenv);
+ view->dttypes = DNS_DTTYPE_ALL;
+
+ /*
+ * Set up some test data
+ */
+ zname = dns_fixedname_initname(&zfname);
+ isc_buffer_constinit(&zb, "example.com.", 12);
+ isc_buffer_add(&zb, 12);
+ result = dns_name_fromtext(zname, &zb, NULL, 0, NULL);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ memset(&zr, 0, sizeof(zr));
+ isc_buffer_init(&zb, zone, sizeof(zone));
+ result = dns_compress_init(&cctx, -1, mctx);
+ dns_compress_setmethods(&cctx, DNS_COMPRESS_NONE);
+ result = dns_name_towire(zname, &cctx, &zb);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ dns_compress_invalidate(&cctx);
+ isc_buffer_usedregion(&zb, &zr);
+
+ in.s_addr = inet_addr("10.53.0.1");
+ isc_sockaddr_fromin(&qaddr, &in, 2112);
+ in.s_addr = inet_addr("10.53.0.2");
+ isc_sockaddr_fromin(&raddr, &in, 2112);
+
+ isc_stdtime_get(&now);
+ isc_time_set(&p, now - 3600, 0); /* past */
+ isc_time_set(&f, now + 3600, 0); /* future */
+
+ result = dns_test_getdata("testdata/dnstap/query.auth",
+ qambuffer, sizeof(qambuffer), &qasize);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ isc_buffer_init(&qamsg, qambuffer, qasize);
+ isc_buffer_add(&qamsg, qasize);
+
+ result = dns_test_getdata("testdata/dnstap/response.auth",
+ rambuffer, sizeof(rambuffer), &rasize);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ isc_buffer_init(&ramsg, rambuffer, rasize);
+ isc_buffer_add(&ramsg, rasize);
+
+ result = dns_test_getdata("testdata/dnstap/query.recursive", qrmbuffer,
+ sizeof(qrmbuffer), &qrsize);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ isc_buffer_init(&qrmsg, qrmbuffer, qrsize);
+ isc_buffer_add(&qrmsg, qrsize);
+
+ result = dns_test_getdata("testdata/dnstap/response.recursive",
+ rrmbuffer, sizeof(rrmbuffer), &rrsize);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ isc_buffer_init(&rrmsg, rrmbuffer, rrsize);
+ isc_buffer_add(&rrmsg, rrsize);
+
+ for (dt = DNS_DTTYPE_SQ; dt <= DNS_DTTYPE_TR; dt <<= 1) {
+ isc_buffer_t *m;
+ isc_sockaddr_t *q = &qaddr, *r = &raddr;
+
+ switch (dt) {
+ case DNS_DTTYPE_AQ:
+ m = &qamsg;
+ break;
+ case DNS_DTTYPE_AR:
+ m = &ramsg;
+ break;
+ default:
+ m = &qrmsg;
+ if ((dt & DNS_DTTYPE_RESPONSE) != 0)
+ m = &ramsg;
+ break;
+ }
+
+ dns_dt_send(view, dt, q, r, false, &zr, &p, &f, m);
+ dns_dt_send(view, dt, q, r, false, &zr, NULL, &f, m);
+ dns_dt_send(view, dt, q, r, false, &zr, &p, NULL, m);
+ dns_dt_send(view, dt, q, r, false, &zr, NULL, NULL, m);
+ dns_dt_send(view, dt, q, r, true, &zr, &p, &f, m);
+ dns_dt_send(view, dt, q, r, true, &zr, NULL, &f, m);
+ dns_dt_send(view, dt, q, r, true, &zr, &p, NULL, m);
+ dns_dt_send(view, dt, q, r, true, &zr, NULL, NULL, m);
+ }
+
+ dns_dt_detach(&view->dtenv);
+ dns_dt_detach(&dtenv);
+ dns_dt_shutdown();
+ dns_view_detach(&view);
+
+ /*
+ * XXX now read back and check content.
+ */
+
+ result = dns_dt_open(TAPFILE, dns_dtmode_file, mctx, &handle);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ while (dns_dt_getframe(handle, &data, &dsize) == ISC_R_SUCCESS) {
+ dns_dtdata_t *dtdata = NULL;
+ isc_region_t r;
+ static dns_dtmsgtype_t expected = DNS_DTTYPE_SQ;
+ static int n = 0;
+
+ r.base = data;
+ r.length = dsize;
+
+ result = dns_dt_parse(mctx, &r, &dtdata);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ if (result != ISC_R_SUCCESS) {
+ n++;
+ continue;
+ }
+
+ ATF_CHECK_EQ(dtdata->type, expected);
+ if (++n % 8 == 0)
+ expected <<= 1;
+
+ dns_dtdata_free(&dtdata);
+ }
+
+ if (fopt != NULL)
+ fstrm_iothr_options_destroy(&fopt);
+ if (handle != NULL)
+ dns_dt_close(&handle);
+ cleanup();
+
+ dns_test_end();
+}
+
+ATF_TC(totext);
+ATF_TC_HEAD(totext, tc) {
+ atf_tc_set_md_var(tc, "descr", "dnstap message to text");
+}
+ATF_TC_BODY(totext, tc) {
+ isc_result_t result;
+ dns_dthandle_t *handle = NULL;
+ uint8_t *data;
+ size_t dsize;
+ FILE *fp = NULL;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, true);
+ ATF_REQUIRE(result == ISC_R_SUCCESS);
+
+ result = dns_dt_open(TAPSAVED, dns_dtmode_file, mctx, &handle);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = isc_stdio_open(TAPTEXT, "r", &fp);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ /* make sure text conversion gets the right local time */
+ setenv("TZ", "PST8", 1);
+
+ while (dns_dt_getframe(handle, &data, &dsize) == ISC_R_SUCCESS) {
+ dns_dtdata_t *dtdata = NULL;
+ isc_buffer_t *b = NULL;
+ isc_region_t r;
+ char s[BUFSIZ], *p;
+
+ r.base = data;
+ r.length = dsize;
+
+ /* read the corresponding line of text */
+ p = fgets(s, sizeof(s), fp);
+ ATF_CHECK_EQ(p, s);
+ if (p == NULL)
+ break;
+
+ p = strchr(p, '\n');
+ if (p != NULL)
+ *p = '\0';
+
+ /* parse dnstap frame */
+ result = dns_dt_parse(mctx, &r, &dtdata);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ if (result != ISC_R_SUCCESS)
+ continue;
+
+ isc_buffer_allocate(mctx, &b, 2048);
+ ATF_CHECK(b != NULL);
+ if (b == NULL)
+ break;
+
+ /* convert to text and compare */
+ result = dns_dt_datatotext(dtdata, &b);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ ATF_CHECK_STREQ((char *) isc_buffer_base(b), s);
+
+ dns_dtdata_free(&dtdata);
+ isc_buffer_free(&b);
+ }
+
+ if (handle != NULL)
+ dns_dt_close(&handle);
+ cleanup();
+
+ dns_test_end();
+}
+
+#else
+ATF_TC(untested);
+ATF_TC_HEAD(untested, tc) {
+ atf_tc_set_md_var(tc, "descr", "skipping dnstap test");
+}
+ATF_TC_BODY(untested, tc) {
+ UNUSED(tc);
+ atf_tc_skip("dnstap not available");
+}
+#endif
+
+/*
+ * Main
+ */
+ATF_TP_ADD_TCS(tp) {
+#ifdef HAVE_DNSTAP
+ ATF_TP_ADD_TC(tp, create);
+ ATF_TP_ADD_TC(tp, send);
+ ATF_TP_ADD_TC(tp, totext);
+#else
+ ATF_TP_ADD_TC(tp, untested);
+#endif
+
+ return (atf_no_error());
+}
diff --git a/lib/dns/tests/dnstest.c b/lib/dns/tests/dnstest.c
new file mode 100644
index 0000000..51bb90b
--- /dev/null
+++ b/lib/dns/tests/dnstest.c
@@ -0,0 +1,596 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <atf-c.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <isc/app.h>
+#include <isc/buffer.h>
+#include <isc/entropy.h>
+#include <isc/file.h>
+#include <isc/hash.h>
+#include <isc/hex.h>
+#include <isc/lex.h>
+#include <isc/mem.h>
+#include <isc/os.h>
+#include <isc/print.h>
+#include <isc/string.h>
+#include <isc/socket.h>
+#include <isc/stdio.h>
+#include <isc/task.h>
+#include <isc/timer.h>
+#include <isc/util.h>
+
+#include <dns/db.h>
+#include <dns/fixedname.h>
+#include <dns/log.h>
+#include <dns/name.h>
+#include <dns/result.h>
+#include <dns/view.h>
+#include <dns/zone.h>
+
+#include "dnstest.h"
+
+isc_mem_t *mctx = NULL;
+isc_entropy_t *ectx = NULL;
+isc_log_t *lctx = NULL;
+isc_taskmgr_t *taskmgr = NULL;
+isc_task_t *maintask = NULL;
+isc_timermgr_t *timermgr = NULL;
+isc_socketmgr_t *socketmgr = NULL;
+dns_zonemgr_t *zonemgr = NULL;
+bool app_running = false;
+int ncpus;
+bool debug_mem_record = true;
+
+static bool hash_active = false, dst_active = false;
+
+/*
+ * Logging categories: this needs to match the list in bin/named/log.c.
+ */
+static isc_logcategory_t categories[] = {
+ { "", 0 },
+ { "client", 0 },
+ { "network", 0 },
+ { "update", 0 },
+ { "queries", 0 },
+ { "unmatched", 0 },
+ { "update-security", 0 },
+ { "query-errors", 0 },
+ { NULL, 0 }
+};
+
+static void
+cleanup_managers(void) {
+ if (app_running)
+ isc_app_finish();
+ if (socketmgr != NULL)
+ isc_socketmgr_destroy(&socketmgr);
+ if (maintask != NULL)
+ isc_task_destroy(&maintask);
+ if (taskmgr != NULL)
+ isc_taskmgr_destroy(&taskmgr);
+ if (timermgr != NULL)
+ isc_timermgr_destroy(&timermgr);
+}
+
+static isc_result_t
+create_managers(void) {
+ isc_result_t result;
+#ifdef ISC_PLATFORM_USETHREADS
+ ncpus = isc_os_ncpus();
+#else
+ ncpus = 1;
+#endif
+
+ CHECK(isc_taskmgr_create(mctx, ncpus, 0, &taskmgr));
+ CHECK(isc_timermgr_create(mctx, &timermgr));
+ CHECK(isc_socketmgr_create(mctx, &socketmgr));
+ CHECK(isc_task_create(taskmgr, 0, &maintask));
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ cleanup_managers();
+ return (result);
+}
+
+isc_result_t
+dns_test_begin(FILE *logfile, bool start_managers) {
+ isc_result_t result;
+
+ if (start_managers)
+ CHECK(isc_app_start());
+ if (debug_mem_record)
+ isc_mem_debugging |= ISC_MEM_DEBUGRECORD;
+ CHECK(isc_mem_create(0, 0, &mctx));
+ CHECK(isc_entropy_create(mctx, &ectx));
+
+ CHECK(isc_hash_create(mctx, ectx, DNS_NAME_MAXWIRE));
+ hash_active = true;
+
+ CHECK(dst_lib_init(mctx, ectx, ISC_ENTROPY_BLOCKING));
+ dst_active = true;
+
+ if (logfile != NULL) {
+ isc_logdestination_t destination;
+ isc_logconfig_t *logconfig = NULL;
+
+ CHECK(isc_log_create(mctx, &lctx, &logconfig));
+ isc_log_registercategories(lctx, categories);
+ isc_log_setcontext(lctx);
+ dns_log_init(lctx);
+ dns_log_setcontext(lctx);
+
+ destination.file.stream = logfile;
+ destination.file.name = NULL;
+ destination.file.versions = ISC_LOG_ROLLNEVER;
+ destination.file.maximum_size = 0;
+ CHECK(isc_log_createchannel(logconfig, "stderr",
+ ISC_LOG_TOFILEDESC,
+ ISC_LOG_DYNAMIC,
+ &destination, 0));
+ CHECK(isc_log_usechannel(logconfig, "stderr", NULL, NULL));
+ }
+
+ dns_result_register();
+
+ if (start_managers)
+ CHECK(create_managers());
+
+ /*
+ * atf-run changes us to a /tmp directory, so tests
+ * that access test data files must first chdir to the proper
+ * location.
+ */
+ if (chdir(TESTS) == -1)
+ CHECK(ISC_R_FAILURE);
+
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ dns_test_end();
+ return (result);
+}
+
+void
+dns_test_end(void) {
+ if (dst_active) {
+ dst_lib_destroy();
+ dst_active = false;
+ }
+ if (hash_active) {
+ isc_hash_destroy();
+ hash_active = false;
+ }
+ if (ectx != NULL)
+ isc_entropy_detach(&ectx);
+
+ cleanup_managers();
+
+ if (lctx != NULL)
+ isc_log_destroy(&lctx);
+
+ if (mctx != NULL)
+ isc_mem_destroy(&mctx);
+}
+
+/*
+ * Create a view.
+ */
+isc_result_t
+dns_test_makeview(const char *name, dns_view_t **viewp) {
+ isc_result_t result;
+ dns_view_t *view = NULL;
+
+ CHECK(dns_view_create(mctx, dns_rdataclass_in, name, &view));
+ *viewp = view;
+
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (view != NULL)
+ dns_view_detach(&view);
+ return (result);
+}
+
+isc_result_t
+dns_test_makezone(const char *name, dns_zone_t **zonep, dns_view_t *view,
+ bool createview)
+{
+ dns_fixedname_t fixed_origin;
+ dns_zone_t *zone = NULL;
+ isc_result_t result;
+ dns_name_t *origin;
+
+ REQUIRE(view == NULL || !createview);
+
+ /*
+ * Create the zone structure.
+ */
+ result = dns_zone_create(&zone, mctx);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ /*
+ * Set zone type and origin.
+ */
+ dns_zone_settype(zone, dns_zone_master);
+ origin = dns_fixedname_initname(&fixed_origin);
+ result = dns_name_fromstring(origin, name, 0, NULL);
+ if (result != ISC_R_SUCCESS) {
+ goto detach_zone;
+ }
+ result = dns_zone_setorigin(zone, origin);
+ if (result != ISC_R_SUCCESS) {
+ goto detach_zone;
+ }
+
+ /*
+ * If requested, create a view.
+ */
+ if (createview) {
+ result = dns_test_makeview("view", &view);
+ if (result != ISC_R_SUCCESS) {
+ goto detach_zone;
+ }
+ }
+
+ /*
+ * If a view was passed as an argument or created above, attach the
+ * created zone to it. Otherwise, set the zone's class to IN.
+ */
+ if (view != NULL) {
+ dns_zone_setview(zone, view);
+ dns_zone_setclass(zone, view->rdclass);
+ dns_view_addzone(view, zone);
+ } else {
+ dns_zone_setclass(zone, dns_rdataclass_in);
+ }
+
+ *zonep = zone;
+
+ return (ISC_R_SUCCESS);
+
+ detach_zone:
+ dns_zone_detach(&zone);
+
+ return (result);
+}
+
+isc_result_t
+dns_test_setupzonemgr(void) {
+ isc_result_t result;
+ REQUIRE(zonemgr == NULL);
+
+ result = dns_zonemgr_create(mctx, taskmgr, timermgr, socketmgr,
+ &zonemgr);
+ return (result);
+}
+
+isc_result_t
+dns_test_managezone(dns_zone_t *zone) {
+ isc_result_t result;
+ REQUIRE(zonemgr != NULL);
+
+ result = dns_zonemgr_setsize(zonemgr, 1);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = dns_zonemgr_managezone(zonemgr, zone);
+ return (result);
+}
+
+void
+dns_test_releasezone(dns_zone_t *zone) {
+ REQUIRE(zonemgr != NULL);
+ dns_zonemgr_releasezone(zonemgr, zone);
+}
+
+void
+dns_test_closezonemgr(void) {
+ REQUIRE(zonemgr != NULL);
+
+ dns_zonemgr_shutdown(zonemgr);
+ dns_zonemgr_detach(&zonemgr);
+}
+
+/*
+ * Sleep for 'usec' microseconds.
+ */
+void
+dns_test_nap(uint32_t usec) {
+#ifdef HAVE_NANOSLEEP
+ struct timespec ts;
+
+ ts.tv_sec = usec / 1000000;
+ ts.tv_nsec = (usec % 1000000) * 1000;
+ nanosleep(&ts, NULL);
+#elif HAVE_USLEEP
+ usleep(usec);
+#else
+ /*
+ * No fractional-second sleep function is available, so we
+ * round up to the nearest second and sleep instead
+ */
+ sleep((usec / 1000000) + 1);
+#endif
+}
+
+isc_result_t
+dns_test_loaddb(dns_db_t **db, dns_dbtype_t dbtype, const char *origin,
+ const char *testfile)
+{
+ isc_result_t result;
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+
+ name = dns_fixedname_initname(&fixed);
+
+ result = dns_name_fromstring(name, origin, 0, NULL);
+ if (result != ISC_R_SUCCESS)
+ return(result);
+
+ result = dns_db_create(mctx, "rbt", name, dbtype, dns_rdataclass_in,
+ 0, NULL, db);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = dns_db_load(*db, testfile);
+ return (result);
+}
+
+static int
+fromhex(char c) {
+ if (c >= '0' && c <= '9')
+ return (c - '0');
+ else if (c >= 'a' && c <= 'f')
+ return (c - 'a' + 10);
+ else if (c >= 'A' && c <= 'F')
+ return (c - 'A' + 10);
+
+ printf("bad input format: %02x\n", c);
+ exit(3);
+ /* NOTREACHED */
+}
+
+/*
+ * Format contents of given memory region as a hex string, using the buffer
+ * of length 'buflen' pointed to by 'buf'. 'buflen' must be at least three
+ * times 'len'. Always returns 'buf'.
+ */
+char *
+dns_test_tohex(const unsigned char *data, size_t len, char *buf, size_t buflen)
+{
+ isc_constregion_t source = {
+ .base = data,
+ .length = len
+ };
+ isc_buffer_t target;
+ isc_result_t result;
+
+ memset(buf, 0, buflen);
+ isc_buffer_init(&target, buf, buflen);
+ result = isc_hex_totext((isc_region_t *)&source, 1, " ", &target);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ return (buf);
+}
+
+isc_result_t
+dns_test_getdata(const char *file, unsigned char *buf,
+ size_t bufsiz, size_t *sizep)
+{
+ isc_result_t result;
+ unsigned char *bp;
+ char *rp, *wp;
+ char s[BUFSIZ];
+ size_t len, i;
+ FILE *f = NULL;
+ int n;
+
+ result = isc_stdio_open(file, "r", &f);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ bp = buf;
+ while (fgets(s, sizeof(s), f) != NULL) {
+ rp = s;
+ wp = s;
+ len = 0;
+ while (*rp != '\0') {
+ if (*rp == '#')
+ break;
+ if (*rp != ' ' && *rp != '\t' &&
+ *rp != '\r' && *rp != '\n') {
+ *wp++ = *rp;
+ len++;
+ }
+ rp++;
+ }
+ if (len == 0U)
+ continue;
+ if (len % 2 != 0U)
+ CHECK(ISC_R_UNEXPECTEDEND);
+ if (len > bufsiz * 2)
+ CHECK(ISC_R_NOSPACE);
+ rp = s;
+ for (i = 0; i < len; i += 2) {
+ n = fromhex(*rp++);
+ n *= 16;
+ n += fromhex(*rp++);
+ *bp++ = n;
+ }
+ }
+
+
+ *sizep = bp - buf;
+
+ result = ISC_R_SUCCESS;
+
+ cleanup:
+ isc_stdio_close(f);
+ return (result);
+}
+
+isc_result_t
+dns_test_rdatafromstring(dns_rdata_t *rdata, dns_rdataclass_t rdclass,
+ dns_rdatatype_t rdtype, unsigned char *dst,
+ size_t dstlen, const char *src)
+{
+ isc_buffer_t source, target;
+ isc_lex_t *lex = NULL;
+ isc_result_t result;
+ size_t length;
+
+ REQUIRE(rdata != NULL);
+ REQUIRE(DNS_RDATA_INITIALIZED(rdata));
+ REQUIRE(dst != NULL);
+ REQUIRE(src != NULL);
+
+ /*
+ * Set up source to hold the input string.
+ */
+ length = strlen(src);
+ isc_buffer_constinit(&source, src, length);
+ isc_buffer_add(&source, length);
+
+ /*
+ * Create a lexer as one is required by dns_rdata_fromtext().
+ */
+ result = isc_lex_create(mctx, 64, &lex);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ /*
+ * Point lexer at source.
+ */
+ result = isc_lex_openbuffer(lex, &source);
+ if (result != ISC_R_SUCCESS) {
+ goto destroy_lexer;
+ }
+
+ /*
+ * Set up target for storing uncompressed wire form of provided RDATA.
+ */
+ isc_buffer_init(&target, dst, dstlen);
+
+ /*
+ * Parse input string, determining result.
+ */
+ result = dns_rdata_fromtext(rdata, rdclass, rdtype, lex, dns_rootname,
+ 0, NULL, &target, NULL);
+
+ destroy_lexer:
+ isc_lex_destroy(&lex);
+
+ return (result);
+}
+
+void
+dns_test_namefromstring(const char *namestr, dns_fixedname_t *fname) {
+ size_t length;
+ isc_buffer_t *b = NULL;
+ isc_result_t result;
+ dns_name_t *name;
+
+ length = strlen(namestr);
+
+ result = isc_buffer_allocate(mctx, &b, length);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ isc_buffer_putmem(b, (const unsigned char *) namestr, length);
+
+ name = dns_fixedname_initname(fname);
+ ATF_REQUIRE(name != NULL);
+ result = dns_name_fromtext(name, b, dns_rootname, 0, NULL);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ isc_buffer_free(&b);
+}
+
+isc_result_t
+dns_test_difffromchanges(dns_diff_t *diff, const zonechange_t *changes) {
+ isc_result_t result = ISC_R_SUCCESS;
+ unsigned char rdata_buf[1024];
+ dns_difftuple_t *tuple = NULL;
+ isc_consttextregion_t region;
+ dns_rdatatype_t rdatatype;
+ dns_fixedname_t fixedname;
+ dns_rdata_t rdata;
+ dns_name_t *name;
+ size_t i;
+
+ REQUIRE(diff != NULL);
+ REQUIRE(changes != NULL);
+
+ dns_diff_init(mctx, diff);
+
+ for (i = 0; changes[i].owner != NULL; i++) {
+ /*
+ * Parse owner name.
+ */
+ name = dns_fixedname_initname(&fixedname);
+ result = dns_name_fromstring(name, changes[i].owner, 0, mctx);
+ if (result != ISC_R_SUCCESS) {
+ break;
+ }
+
+ /*
+ * Parse RDATA type.
+ */
+ region.base = changes[i].type;
+ region.length = strlen(changes[i].type);
+ result = dns_rdatatype_fromtext(&rdatatype,
+ (isc_textregion_t *)&region);
+ if (result != ISC_R_SUCCESS) {
+ break;
+ }
+
+ /*
+ * Parse RDATA.
+ */
+ dns_rdata_init(&rdata);
+ result = dns_test_rdatafromstring(&rdata, dns_rdataclass_in,
+ rdatatype, rdata_buf,
+ sizeof(rdata_buf),
+ changes[i].rdata);
+ if (result != ISC_R_SUCCESS) {
+ break;
+ }
+
+ /*
+ * Create a diff tuple for the parsed change and append it to
+ * the diff.
+ */
+ result = dns_difftuple_create(mctx, changes[i].op, name,
+ changes[i].ttl, &rdata, &tuple);
+ if (result != ISC_R_SUCCESS) {
+ break;
+ }
+ dns_diff_append(diff, &tuple);
+ }
+
+ if (result != ISC_R_SUCCESS) {
+ dns_diff_clear(diff);
+ }
+
+ return (result);
+}
diff --git a/lib/dns/tests/dnstest.h b/lib/dns/tests/dnstest.h
new file mode 100644
index 0000000..ebbca6c
--- /dev/null
+++ b/lib/dns/tests/dnstest.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/buffer.h>
+#include <isc/entropy.h>
+#include <isc/hash.h>
+#include <isc/log.h>
+#include <isc/mem.h>
+#include <isc/string.h>
+#include <isc/task.h>
+#include <isc/timer.h>
+#include <isc/util.h>
+
+#include <dns/diff.h>
+#include <dns/result.h>
+#include <dns/zone.h>
+
+#define CHECK(r) \
+ do { \
+ result = (r); \
+ if (result != ISC_R_SUCCESS) \
+ goto cleanup; \
+ } while (0)
+
+typedef struct {
+ dns_diffop_t op;
+ const char *owner;
+ dns_ttl_t ttl;
+ const char *type;
+ const char *rdata;
+} zonechange_t;
+
+#define ZONECHANGE_SENTINEL { 0, NULL, 0, NULL, NULL }
+
+extern isc_mem_t *mctx;
+extern isc_entropy_t *ectx;
+extern isc_log_t *lctx;
+extern isc_taskmgr_t *taskmgr;
+extern isc_task_t *maintask;
+extern isc_timermgr_t *timermgr;
+extern isc_socketmgr_t *socketmgr;
+extern dns_zonemgr_t *zonemgr;
+extern bool app_running;
+extern int ncpus;
+extern bool debug_mem_record;
+
+isc_result_t
+dns_test_begin(FILE *logfile, bool create_managers);
+
+void
+dns_test_end(void);
+
+isc_result_t
+dns_test_makeview(const char *name, dns_view_t **viewp);
+
+/*%
+ * Create a zone with origin 'name', return a pointer to the zone object in
+ * 'zonep'.
+ *
+ * If 'view' is set, the returned zone will be assigned to the passed view.
+ * 'createview' must be set to false when 'view' is non-NULL.
+ *
+ * If 'view' is not set and 'createview' is true, a new view is also created
+ * and the returned zone is assigned to it. This imposes two requirements on
+ * the caller: 1) the returned zone has to be subsequently assigned to a zone
+ * manager, otherwise its cleanup will fail, 2) the created view has to be
+ * cleaned up by the caller.
+ *
+ * If 'view' is not set and 'createview' is false, the returned zone will not
+ * be assigned to any view.
+ */
+isc_result_t
+dns_test_makezone(const char *name, dns_zone_t **zonep, dns_view_t *view,
+ bool createview);
+
+isc_result_t
+dns_test_setupzonemgr(void);
+
+isc_result_t
+dns_test_managezone(dns_zone_t *zone);
+
+void
+dns_test_releasezone(dns_zone_t *zone);
+
+void
+dns_test_closezonemgr(void);
+
+void
+dns_test_nap(uint32_t usec);
+
+isc_result_t
+dns_test_loaddb(dns_db_t **db, dns_dbtype_t dbtype, const char *origin,
+ const char *testfile);
+
+isc_result_t
+dns_test_getdata(const char *file, unsigned char *buf,
+ size_t bufsiz, size_t *sizep);
+
+char *
+dns_test_tohex(const unsigned char *data, size_t len, char *buf, size_t buflen);
+
+/*%
+ * Try parsing text form RDATA in "src" (of class "rdclass" and type "rdtype")
+ * into a structure representing that RDATA at "rdata", storing the
+ * uncompressed wire form of that RDATA at "dst", which is "dstlen" bytes long.
+ */
+isc_result_t
+dns_test_rdatafromstring(dns_rdata_t *rdata, dns_rdataclass_t rdclass,
+ dns_rdatatype_t rdtype, unsigned char *dst,
+ size_t dstlen, const char *src);
+
+void
+dns_test_namefromstring(const char *namestr, dns_fixedname_t *fname);
+
+/*%
+ * Given a pointer to an uninitialized dns_diff_t structure in 'diff', make it
+ * contain diff tuples representing zone database changes listed in 'changes'.
+ */
+isc_result_t
+dns_test_difffromchanges(dns_diff_t *diff, const zonechange_t *changes);
diff --git a/lib/dns/tests/dst_test.c b/lib/dns/tests/dst_test.c
new file mode 100644
index 0000000..421ab2f
--- /dev/null
+++ b/lib/dns/tests/dst_test.c
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* ! \file */
+
+#include <config.h>
+
+#include <atf-c.h>
+
+#include <stdbool.h>
+#include <unistd.h>
+
+#include <isc/file.h>
+#include <isc/util.h>
+#include <isc/stdio.h>
+#include <isc/string.h>
+
+#include <dst/dst.h>
+#include <dst/result.h>
+
+#include "../dst_internal.h"
+
+#include "dnstest.h"
+
+ATF_TC(sig);
+ATF_TC_HEAD(sig, tc) {
+ atf_tc_set_md_var(tc, "descr", "signature ineffability");
+}
+
+/*
+ * Read sig in file at path to buf.
+ */
+static isc_result_t
+sig_fromfile(const char *path, isc_buffer_t *buf) {
+ isc_result_t result;
+ size_t rval, len;
+ FILE *fp = NULL;
+ unsigned char val;
+ char *p, *data;
+ off_t size;
+
+ result = isc_stdio_open(path, "rb", &fp);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = isc_file_getsizefd(fileno(fp), &size);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ data = isc_mem_get(mctx, (size + 1));
+ ATF_REQUIRE(data != NULL);
+
+ len = (size_t)size;
+ p = data;
+ while (len != 0U) {
+ result = isc_stdio_read(p, 1, len, fp, &rval);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ len -= rval;
+ p += rval;
+ }
+ isc_stdio_close(fp);
+
+ p = data;
+ len = size;
+ while (len > 0U) {
+ if ((*p == '\r') || (*p == '\n')) {
+ ++p;
+ --len;
+ continue;
+ } else if (len < 2U)
+ goto err;
+ if (('0' <= *p) && (*p <= '9')) {
+ val = *p - '0';
+ } else if (('A' <= *p) && (*p <= 'F')) {
+ val = *p - 'A' + 10;
+ } else {
+ result = ISC_R_BADHEX;
+ goto err;
+ }
+ ++p;
+ val <<= 4;
+ --len;
+ if (('0' <= *p) && (*p <= '9')) {
+ val |= (*p - '0');
+ } else if (('A' <= *p) && (*p <= 'F')) {
+ val |= (*p - 'A' + 10);
+ } else {
+ result = ISC_R_BADHEX;
+ goto err;
+ }
+ ++p;
+ --len;
+ isc_buffer_putuint8(buf, val);
+ }
+
+ result = ISC_R_SUCCESS;
+
+ err:
+ isc_mem_put(mctx, data, size + 1);
+ return (result);
+}
+
+static void
+check_sig(const char *datapath, const char *sigpath, const char *keyname,
+ dns_keytag_t id, dns_secalg_t alg, int type, bool expect)
+{
+ isc_result_t result;
+ size_t rval, len;
+ FILE *fp;
+ dst_key_t *key = NULL;
+ unsigned char sig[512];
+ unsigned char *p;
+ unsigned char *data;
+ off_t size;
+ isc_buffer_t b;
+ isc_buffer_t databuf, sigbuf;
+ isc_region_t datareg, sigreg;
+ dns_fixedname_t fname;
+ dns_name_t *name;
+ dst_context_t *ctx = NULL;
+
+ /*
+ * Read data from file in a form usable by dst_verify.
+ */
+ result = isc_stdio_open(datapath, "rb", &fp);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = isc_file_getsizefd(fileno(fp), &size);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ data = isc_mem_get(mctx, (size + 1));
+ ATF_REQUIRE(data != NULL);
+
+ p = data;
+ len = (size_t)size;
+ do {
+ result = isc_stdio_read(p, 1, len, fp, &rval);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ len -= rval;
+ p += rval;
+ } while (len);
+ isc_stdio_close(fp);
+
+ /*
+ * Read key from file in a form usable by dst_verify.
+ */
+ name = dns_fixedname_initname(&fname);
+ isc_buffer_constinit(&b, keyname, strlen(keyname));
+ isc_buffer_add(&b, strlen(keyname));
+ result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ result = dst_key_fromfile(name, id, alg, type, "testdata/dst",
+ mctx, &key);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ isc_buffer_init(&databuf, data, (unsigned int)size);
+ isc_buffer_add(&databuf, (unsigned int)size);
+ isc_buffer_usedregion(&databuf, &datareg);
+
+ memset(sig, 0, sizeof(sig));
+ isc_buffer_init(&sigbuf, sig, sizeof(sig));
+
+ /*
+ * Read precomputed signature from file in a form usable by dst_verify.
+ */
+ result = sig_fromfile(sigpath, &sigbuf);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ /*
+ * Verify that the key signed the data.
+ */
+ isc_buffer_remainingregion(&sigbuf, &sigreg);
+
+ result = dst_context_create3(key, mctx, DNS_LOGCATEGORY_GENERAL,
+ false, &ctx);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dst_context_adddata(ctx, &datareg);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ result = dst_context_verify(ctx, &sigreg);
+
+ ATF_REQUIRE((expect && (result == ISC_R_SUCCESS)) ||
+ (!expect && (result != ISC_R_SUCCESS)));
+
+
+ isc_mem_put(mctx, data, size + 1);
+ dst_context_destroy(&ctx);
+ dst_key_free(&key);
+
+ return;
+}
+
+ATF_TC_BODY(sig, tc) {
+ isc_result_t result;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ struct {
+ const char *datapath;
+ const char *sigpath;
+ const char *keyname;
+ dns_keytag_t keyid;
+ dns_secalg_t alg;
+ bool expect;
+ } testcases[] = {
+ {
+ "testdata/dst/test1.data",
+ "testdata/dst/test1.dsasig",
+ "test.", 23616, DST_ALG_DSA, true
+ },
+ {
+ "testdata/dst/test1.data",
+ "testdata/dst/test1.rsasig",
+ "test.", 54622, DST_ALG_RSAMD5, true
+ },
+ {
+ /* wrong sig */
+ "testdata/dst/test1.data",
+ "testdata/dst/test1.dsasig",
+ "test.", 54622, DST_ALG_RSAMD5, false
+ },
+ {
+ /* wrong data */
+ "testdata/dst/test2.data",
+ "testdata/dst/test1.dsasig",
+ "test.", 23616, DST_ALG_DSA, false
+ },
+ };
+ unsigned int i;
+
+ for (i = 0; i < (sizeof(testcases)/sizeof(testcases[0])); i++) {
+ if (!dst_algorithm_supported(testcases[i].alg)) {
+ continue;
+ }
+
+ check_sig(testcases[i].datapath,
+ testcases[i].sigpath,
+ testcases[i].keyname,
+ testcases[i].keyid,
+ testcases[i].alg,
+ DST_TYPE_PRIVATE|DST_TYPE_PUBLIC,
+ testcases[i].expect);
+ }
+
+ dns_test_end();
+}
+
+/*
+ * Main
+ */
+ATF_TP_ADD_TCS(tp) {
+ ATF_TP_ADD_TC(tp, sig);
+
+ return (atf_no_error());
+}
diff --git a/lib/dns/tests/geoip_test.c b/lib/dns/tests/geoip_test.c
new file mode 100644
index 0000000..4e8a5f0
--- /dev/null
+++ b/lib/dns/tests/geoip_test.c
@@ -0,0 +1,711 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <atf-c.h>
+
+#include <stdbool.h>
+#include <unistd.h>
+
+#include <isc/print.h>
+#include <isc/string.h>
+#include <isc/types.h>
+
+#include <dns/geoip.h>
+
+#include "dnstest.h"
+
+#ifdef HAVE_GEOIP
+#include <GeoIP.h>
+
+/* We use GeoIP databases from the 'geoip' system test */
+#define TEST_GEOIP_DATA "../../../bin/tests/system/geoip/data"
+
+/*
+ * Helper functions
+ * (Mostly copied from bin/named/geoip.c)
+ */
+static dns_geoip_databases_t geoip = {
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static void
+init_geoip_db(GeoIP **dbp, GeoIPDBTypes edition, GeoIPDBTypes fallback,
+ GeoIPOptions method, const char *name)
+{
+ char *info;
+ GeoIP *db;
+
+ REQUIRE(dbp != NULL);
+
+ db = *dbp;
+
+ if (db != NULL) {
+ GeoIP_delete(db);
+ db = *dbp = NULL;
+ }
+
+ if (! GeoIP_db_avail(edition)) {
+ fprintf(stderr, "GeoIP %s (type %d) DB not available\n",
+ name, edition);
+ goto fail;
+ }
+
+ fprintf(stderr, "initializing GeoIP %s (type %d) DB\n",
+ name, edition);
+
+ db = GeoIP_open_type(edition, method);
+ if (db == NULL) {
+ fprintf(stderr,
+ "failed to initialize GeoIP %s (type %d) DB%s\n",
+ name, edition, fallback == 0
+ ? "; geoip matches using this database will fail"
+ : "");
+ goto fail;
+ }
+
+ info = GeoIP_database_info(db);
+ if (info != NULL)
+ fprintf(stderr, "%s\n", info);
+
+ *dbp = db;
+ return;
+
+ fail:
+ if (fallback != 0)
+ init_geoip_db(dbp, fallback, 0, method, name);
+}
+
+static void
+load_geoip(const char *dir) {
+ GeoIPOptions method;
+
+#ifdef _WIN32
+ method = GEOIP_STANDARD;
+#else
+ method = GEOIP_MMAP_CACHE;
+#endif
+
+ if (dir != NULL) {
+ char *p;
+ DE_CONST(dir, p);
+ GeoIP_setup_custom_directory(p);
+ }
+
+ init_geoip_db(&geoip.country_v4, GEOIP_COUNTRY_EDITION, 0,
+ method, "Country (IPv4)");
+#ifdef HAVE_GEOIP_V6
+ init_geoip_db(&geoip.country_v6, GEOIP_COUNTRY_EDITION_V6, 0,
+ method, "Country (IPv6)");
+#endif
+
+ init_geoip_db(&geoip.city_v4, GEOIP_CITY_EDITION_REV1,
+ GEOIP_CITY_EDITION_REV0, method, "City (IPv4)");
+#if defined(HAVE_GEOIP_V6) && defined(HAVE_GEOIP_CITY_V6)
+ init_geoip_db(&geoip.city_v6, GEOIP_CITY_EDITION_REV1_V6,
+ GEOIP_CITY_EDITION_REV0_V6, method, "City (IPv6)");
+#endif
+
+ init_geoip_db(&geoip.region, GEOIP_REGION_EDITION_REV1,
+ GEOIP_REGION_EDITION_REV0, method, "Region");
+ init_geoip_db(&geoip.isp, GEOIP_ISP_EDITION, 0,
+ method, "ISP");
+ init_geoip_db(&geoip.org, GEOIP_ORG_EDITION, 0,
+ method, "Org");
+ init_geoip_db(&geoip.as, GEOIP_ASNUM_EDITION, 0,
+ method, "AS");
+ init_geoip_db(&geoip.domain, GEOIP_DOMAIN_EDITION, 0,
+ method, "Domain");
+ init_geoip_db(&geoip.netspeed, GEOIP_NETSPEED_EDITION, 0,
+ method, "NetSpeed");
+}
+
+static bool
+do_lookup_string(const char *addr, uint8_t *scope,
+ dns_geoip_subtype_t subtype, const char *string)
+{
+ dns_geoip_elem_t elt;
+ struct in_addr in4;
+ isc_netaddr_t na;
+
+ inet_pton(AF_INET, addr, &in4);
+ isc_netaddr_fromin(&na, &in4);
+
+ elt.subtype = subtype;
+ strlcpy(elt.as_string, string, sizeof(elt.as_string));
+
+ return (dns_geoip_match(&na, scope, &geoip, &elt));
+}
+
+static bool
+do_lookup_string_v6(const char *addr, uint8_t *scope,
+ dns_geoip_subtype_t subtype, const char *string)
+{
+ dns_geoip_elem_t elt;
+ struct in6_addr in6;
+ isc_netaddr_t na;
+
+ inet_pton(AF_INET6, addr, &in6);
+ isc_netaddr_fromin6(&na, &in6);
+
+ elt.subtype = subtype;
+ strlcpy(elt.as_string, string, sizeof(elt.as_string));
+
+ return (dns_geoip_match(&na, scope, &geoip, &elt));
+}
+
+static bool
+do_lookup_int(const char *addr, uint8_t *scope,
+ dns_geoip_subtype_t subtype, int id)
+{
+ dns_geoip_elem_t elt;
+ struct in_addr in4;
+ isc_netaddr_t na;
+
+ inet_pton(AF_INET, addr, &in4);
+ isc_netaddr_fromin(&na, &in4);
+
+ elt.subtype = subtype;
+ elt.as_int = id;
+
+ return (dns_geoip_match(&na, scope, &geoip, &elt));
+}
+
+/*
+ * Individual unit tests
+ */
+
+/* GeoIP country matching */
+ATF_TC(country);
+ATF_TC_HEAD(country, tc) {
+ atf_tc_set_md_var(tc, "descr", "test country database matching");
+}
+ATF_TC_BODY(country, tc) {
+ isc_result_t result;
+ bool match;
+ uint8_t scope;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, true);
+ ATF_REQUIRE(result == ISC_R_SUCCESS);
+
+ /* Use databases from the geoip system test */
+ load_geoip(TEST_GEOIP_DATA);
+
+ if (geoip.country_v4 == NULL) {
+ dns_test_end();
+ atf_tc_skip("Database not available");
+ }
+
+ match = do_lookup_string("10.53.0.1", &scope,
+ dns_geoip_country_code, "AU");
+ ATF_CHECK(match);
+ ATF_CHECK_EQ(scope, 32);
+
+ match = do_lookup_string("10.53.0.1", &scope,
+ dns_geoip_country_code3, "AUS");
+ ATF_CHECK(match);
+ ATF_CHECK_EQ(scope, 32);
+
+ match = do_lookup_string("10.53.0.1", &scope,
+ dns_geoip_country_name, "Australia");
+ ATF_CHECK(match);
+ ATF_CHECK_EQ(scope, 32);
+
+ match = do_lookup_string("192.0.2.128", &scope,
+ dns_geoip_country_code, "O1");
+ ATF_CHECK(match);
+ ATF_CHECK_EQ(scope, 24);
+
+ match = do_lookup_string("192.0.2.128", &scope,
+ dns_geoip_country_name, "Other");
+ ATF_CHECK(match);
+ ATF_CHECK_EQ(scope, 24);
+
+ dns_test_end();
+}
+
+/* GeoIP country (ipv6) matching */
+ATF_TC(country_v6);
+ATF_TC_HEAD(country_v6, tc) {
+ atf_tc_set_md_var(tc, "descr", "test country (ipv6) database matching");
+}
+ATF_TC_BODY(country_v6, tc) {
+ isc_result_t result;
+ bool match;
+ uint8_t scope;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, true);
+ ATF_REQUIRE(result == ISC_R_SUCCESS);
+
+ /* Use databases from the geoip system test */
+ load_geoip(TEST_GEOIP_DATA);
+
+ if (geoip.country_v6 == NULL) {
+ dns_test_end();
+ atf_tc_skip("Database not available");
+ }
+
+ match = do_lookup_string_v6("fd92:7065:b8e:ffff::1", &scope,
+ dns_geoip_country_code, "AU");
+ ATF_CHECK(match);
+ ATF_CHECK_EQ(scope, 128);
+
+ match = do_lookup_string_v6("fd92:7065:b8e:ffff::1", &scope,
+ dns_geoip_country_code3, "AUS");
+ ATF_CHECK(match);
+ ATF_CHECK_EQ(scope, 128);
+
+ match = do_lookup_string_v6("fd92:7065:b8e:ffff::1", &scope,
+ dns_geoip_country_name, "Australia");
+ ATF_CHECK(match);
+ ATF_CHECK_EQ(scope, 128);
+
+ dns_test_end();
+}
+
+/* GeoIP city (ipv4) matching */
+ATF_TC(city);
+ATF_TC_HEAD(city, tc) {
+ atf_tc_set_md_var(tc, "descr", "test city database matching");
+}
+ATF_TC_BODY(city, tc) {
+ isc_result_t result;
+ bool match;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, true);
+ ATF_REQUIRE(result == ISC_R_SUCCESS);
+
+ /* Use databases from the geoip system test */
+ load_geoip(TEST_GEOIP_DATA);
+
+ if (geoip.city_v4 == NULL) {
+ dns_test_end();
+ atf_tc_skip("Database not available");
+ }
+
+ match = do_lookup_string("10.53.0.1", NULL,
+ dns_geoip_city_continentcode, "NA");
+ ATF_CHECK(match);
+
+ match = do_lookup_string("10.53.0.1", NULL,
+ dns_geoip_city_countrycode, "US");
+ ATF_CHECK(match);
+
+ match = do_lookup_string("10.53.0.1", NULL,
+ dns_geoip_city_countrycode3, "USA");
+ ATF_CHECK(match);
+
+ match = do_lookup_string("10.53.0.1", NULL,
+ dns_geoip_city_countryname, "United States");
+ ATF_CHECK(match);
+
+ match = do_lookup_string("10.53.0.1", NULL,
+ dns_geoip_city_region, "CA");
+ ATF_CHECK(match);
+
+ match = do_lookup_string("10.53.0.1", NULL,
+ dns_geoip_city_regionname, "California");
+ ATF_CHECK(match);
+
+ match = do_lookup_string("10.53.0.1", NULL,
+ dns_geoip_city_name, "Redwood City");
+ ATF_CHECK(match);
+
+ match = do_lookup_string("10.53.0.1", NULL,
+ dns_geoip_city_postalcode, "94063");
+ ATF_CHECK(match);
+
+ match = do_lookup_int("10.53.0.1", NULL, dns_geoip_city_areacode, 650);
+ ATF_CHECK(match);
+
+ match = do_lookup_int("10.53.0.1", NULL, dns_geoip_city_metrocode, 807);
+ ATF_CHECK(match);
+
+ dns_test_end();
+}
+
+/* GeoIP city (ipv6) matching */
+ATF_TC(city_v6);
+ATF_TC_HEAD(city_v6, tc) {
+ atf_tc_set_md_var(tc, "descr", "test city (ipv6) database matching");
+}
+ATF_TC_BODY(city_v6, tc) {
+ isc_result_t result;
+ bool match;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, true);
+ ATF_REQUIRE(result == ISC_R_SUCCESS);
+
+ /* Use databases from the geoip system test */
+ load_geoip(TEST_GEOIP_DATA);
+
+ if (geoip.city_v6 == NULL) {
+ dns_test_end();
+ atf_tc_skip("Database not available");
+ }
+
+ match = do_lookup_string_v6("fd92:7065:b8e:ffff::1", NULL,
+ dns_geoip_city_continentcode, "NA");
+ ATF_CHECK(match);
+
+ match = do_lookup_string_v6("fd92:7065:b8e:ffff::1", NULL,
+ dns_geoip_city_countrycode, "US");
+ ATF_CHECK(match);
+
+ match = do_lookup_string_v6("fd92:7065:b8e:ffff::1", NULL,
+ dns_geoip_city_countrycode3, "USA");
+ ATF_CHECK(match);
+
+ match = do_lookup_string_v6("fd92:7065:b8e:ffff::1", NULL,
+ dns_geoip_city_countryname,
+ "United States");
+ ATF_CHECK(match);
+
+ match = do_lookup_string_v6("fd92:7065:b8e:ffff::1", NULL,
+ dns_geoip_city_region, "CA");
+ ATF_CHECK(match);
+
+ match = do_lookup_string_v6("fd92:7065:b8e:ffff::1", NULL,
+ dns_geoip_city_regionname, "California");
+ ATF_CHECK(match);
+
+ match = do_lookup_string_v6("fd92:7065:b8e:ffff::1", NULL,
+ dns_geoip_city_name, "Redwood City");
+ ATF_CHECK(match);
+
+ match = do_lookup_string_v6("fd92:7065:b8e:ffff::1", NULL,
+ dns_geoip_city_postalcode, "94063");
+ ATF_CHECK(match);
+
+ dns_test_end();
+}
+
+
+/* GeoIP region matching */
+ATF_TC(region);
+ATF_TC_HEAD(region, tc) {
+ atf_tc_set_md_var(tc, "descr", "test region database matching");
+}
+ATF_TC_BODY(region, tc) {
+ isc_result_t result;
+ bool match;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, true);
+ ATF_REQUIRE(result == ISC_R_SUCCESS);
+
+ /* Use databases from the geoip system test */
+ load_geoip(TEST_GEOIP_DATA);
+
+ if (geoip.region == NULL) {
+ dns_test_end();
+ atf_tc_skip("Database not available");
+ }
+
+ match = do_lookup_string("10.53.0.1", NULL,
+ dns_geoip_region_code, "CA");
+ ATF_CHECK(match);
+
+ match = do_lookup_string("10.53.0.1", NULL,
+ dns_geoip_region_name, "California");
+ ATF_CHECK(match);
+
+ match = do_lookup_string("10.53.0.1", NULL,
+ dns_geoip_region_countrycode, "US");
+ ATF_CHECK(match);
+
+ dns_test_end();
+}
+
+/*
+ * GeoIP best-database matching
+ * (With no specified databse and a city database available, answers
+ * should come from city database. With city database unavailable, region
+ * database. Region database unavailable, country database.)
+ */
+ATF_TC(best);
+ATF_TC_HEAD(best, tc) {
+ atf_tc_set_md_var(tc, "descr", "test best database matching");
+}
+ATF_TC_BODY(best, tc) {
+ isc_result_t result;
+ bool match;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, true);
+ ATF_REQUIRE(result == ISC_R_SUCCESS);
+
+ /* Use databases from the geoip system test */
+ load_geoip(TEST_GEOIP_DATA);
+
+ if (geoip.region == NULL) {
+ dns_test_end();
+ atf_tc_skip("Database not available");
+ }
+
+ match = do_lookup_string("10.53.0.4", NULL,
+ dns_geoip_countrycode, "US");
+ ATF_CHECK(match);
+
+ match = do_lookup_string("10.53.0.4", NULL,
+ dns_geoip_countrycode3, "USA");
+ ATF_CHECK(match);
+
+ match = do_lookup_string("10.53.0.4", NULL,
+ dns_geoip_countryname, "United States");
+ ATF_CHECK(match);
+
+ match = do_lookup_string("10.53.0.4", NULL,
+ dns_geoip_regionname, "Virginia");
+ ATF_CHECK(match);
+
+ match = do_lookup_string("10.53.0.4", NULL,
+ dns_geoip_region, "VA");
+ ATF_CHECK(match);
+
+ GeoIP_delete(geoip.city_v4);
+ geoip.city_v4 = NULL;
+
+ match = do_lookup_string("10.53.0.4", NULL,
+ dns_geoip_countrycode, "AU");
+ ATF_CHECK(match);
+
+ /*
+ * Note, region doesn't support code3 or countryname, so
+ * the next two would be answered from the country database instead
+ */
+ match = do_lookup_string("10.53.0.4", NULL,
+ dns_geoip_countrycode3, "CAN");
+ ATF_CHECK(match);
+
+ match = do_lookup_string("10.53.0.4", NULL,
+ dns_geoip_countryname, "Canada");
+ ATF_CHECK(match);
+
+ GeoIP_delete(geoip.region);
+ geoip.region = NULL;
+
+ match = do_lookup_string("10.53.0.4", NULL,
+ dns_geoip_countrycode, "CA");
+ ATF_CHECK(match);
+
+ match = do_lookup_string("10.53.0.4", NULL,
+ dns_geoip_countrycode3, "CAN");
+ ATF_CHECK(match);
+
+ match = do_lookup_string("10.53.0.4", NULL,
+ dns_geoip_countryname, "Canada");
+ ATF_CHECK(match);
+
+ dns_test_end();
+}
+
+
+/* GeoIP asnum matching */
+ATF_TC(asnum);
+ATF_TC_HEAD(asnum, tc) {
+ atf_tc_set_md_var(tc, "descr", "test asnum database matching");
+}
+ATF_TC_BODY(asnum, tc) {
+ isc_result_t result;
+ bool match;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, true);
+ ATF_REQUIRE(result == ISC_R_SUCCESS);
+
+ /* Use databases from the geoip system test */
+ load_geoip(TEST_GEOIP_DATA);
+
+ if (geoip.as == NULL) {
+ dns_test_end();
+ atf_tc_skip("Database not available");
+ }
+
+
+ match = do_lookup_string("10.53.0.3", NULL, dns_geoip_as_asnum,
+ "AS100003 Three Network Labs");
+ ATF_CHECK(match);
+
+ dns_test_end();
+}
+
+/* GeoIP isp matching */
+ATF_TC(isp);
+ATF_TC_HEAD(isp, tc) {
+ atf_tc_set_md_var(tc, "descr", "test isp database matching");
+}
+ATF_TC_BODY(isp, tc) {
+ isc_result_t result;
+ bool match;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, true);
+ ATF_REQUIRE(result == ISC_R_SUCCESS);
+
+ /* Use databases from the geoip system test */
+ load_geoip(TEST_GEOIP_DATA);
+
+ if (geoip.isp == NULL) {
+ dns_test_end();
+ atf_tc_skip("Database not available");
+ }
+
+ match = do_lookup_string("10.53.0.1", NULL, dns_geoip_isp_name,
+ "One Systems, Inc.");
+ ATF_CHECK(match);
+
+ dns_test_end();
+}
+
+/* GeoIP org matching */
+ATF_TC(org);
+ATF_TC_HEAD(org, tc) {
+ atf_tc_set_md_var(tc, "descr", "test org database matching");
+}
+ATF_TC_BODY(org, tc) {
+ isc_result_t result;
+ bool match;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, true);
+ ATF_REQUIRE(result == ISC_R_SUCCESS);
+
+ /* Use databases from the geoip system test */
+ load_geoip(TEST_GEOIP_DATA);
+
+ if (geoip.org == NULL) {
+ dns_test_end();
+ atf_tc_skip("Database not available");
+ }
+
+ match = do_lookup_string("10.53.0.2", NULL, dns_geoip_org_name,
+ "Two Technology Ltd.");
+ ATF_CHECK(match);
+
+ dns_test_end();
+}
+
+/* GeoIP domain matching */
+ATF_TC(domain);
+ATF_TC_HEAD(domain, tc) {
+ atf_tc_set_md_var(tc, "descr", "test domain database matching");
+}
+ATF_TC_BODY(domain, tc) {
+ isc_result_t result;
+ bool match;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, true);
+ ATF_REQUIRE(result == ISC_R_SUCCESS);
+
+ /* Use databases from the geoip system test */
+ load_geoip(TEST_GEOIP_DATA);
+
+ if (geoip.domain == NULL) {
+ dns_test_end();
+ atf_tc_skip("Database not available");
+ }
+
+ match = do_lookup_string("10.53.0.4", NULL,
+ dns_geoip_domain_name, "four.com");
+ ATF_CHECK(match);
+
+ dns_test_end();
+}
+
+/* GeoIP netspeed matching */
+ATF_TC(netspeed);
+ATF_TC_HEAD(netspeed, tc) {
+ atf_tc_set_md_var(tc, "descr", "test netspeed database matching");
+}
+ATF_TC_BODY(netspeed, tc) {
+ isc_result_t result;
+ bool match;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, true);
+ ATF_REQUIRE(result == ISC_R_SUCCESS);
+
+ /* Use databases from the geoip system test */
+ load_geoip(TEST_GEOIP_DATA);
+
+ if (geoip.netspeed == NULL) {
+ dns_test_end();
+ atf_tc_skip("Database not available");
+ }
+
+ match = do_lookup_int("10.53.0.1", NULL, dns_geoip_netspeed_id, 0);
+ ATF_CHECK(match);
+
+ match = do_lookup_int("10.53.0.2", NULL, dns_geoip_netspeed_id, 1);
+ ATF_CHECK(match);
+
+ match = do_lookup_int("10.53.0.3", NULL, dns_geoip_netspeed_id, 2);
+ ATF_CHECK(match);
+
+ match = do_lookup_int("10.53.0.4", NULL, dns_geoip_netspeed_id, 3);
+ ATF_CHECK(match);
+
+ dns_test_end();
+}
+#else
+ATF_TC(untested);
+ATF_TC_HEAD(untested, tc) {
+ atf_tc_set_md_var(tc, "descr", "skipping geoip test");
+}
+ATF_TC_BODY(untested, tc) {
+ UNUSED(tc);
+ atf_tc_skip("GeoIP not available");
+}
+#endif
+
+/*
+ * Main
+ */
+ATF_TP_ADD_TCS(tp) {
+#ifdef HAVE_GEOIP
+ ATF_TP_ADD_TC(tp, country);
+ ATF_TP_ADD_TC(tp, country_v6);
+ ATF_TP_ADD_TC(tp, city);
+ ATF_TP_ADD_TC(tp, city_v6);
+ ATF_TP_ADD_TC(tp, region);
+ ATF_TP_ADD_TC(tp, best);
+ ATF_TP_ADD_TC(tp, asnum);
+ ATF_TP_ADD_TC(tp, isp);
+ ATF_TP_ADD_TC(tp, org);
+ ATF_TP_ADD_TC(tp, domain);
+ ATF_TP_ADD_TC(tp, netspeed);
+#else
+ ATF_TP_ADD_TC(tp, untested);
+#endif
+
+ return (atf_no_error());
+}
diff --git a/lib/dns/tests/gost_test.c b/lib/dns/tests/gost_test.c
new file mode 100644
index 0000000..204c6cc
--- /dev/null
+++ b/lib/dns/tests/gost_test.c
@@ -0,0 +1,379 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/* ! \file */
+
+#include <config.h>
+
+#include <atf-c.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include <isc/util.h>
+#include <isc/print.h>
+#include <isc/string.h>
+
+#include "dnstest.h"
+
+#ifdef HAVE_OPENSSL_GOST
+#include "../dst_gost.h"
+#include <openssl/err.h>
+#include <openssl/objects.h>
+#include <openssl/rsa.h>
+#include <openssl/engine.h>
+#include <openssl/bn.h>
+#endif
+
+#ifdef HAVE_PKCS11_GOST
+#include "../dst_gost.h"
+#include <pk11/internal.h>
+#define WANT_GOST_PARAMS
+#include <pk11/constants.h>
+#include <pkcs11/pkcs11.h>
+#endif
+
+#if defined(HAVE_OPENSSL_GOST) || defined(HAVE_PKCS11_GOST)
+/*
+ * Test data from Wikipedia GOST (hash function)
+ */
+
+unsigned char digest[ISC_GOST_DIGESTLENGTH];
+unsigned char buffer[1024];
+const char *s;
+char str[2 * ISC_GOST_DIGESTLENGTH + 3];
+int i = 0;
+
+/*
+ * Precondition: a hexadecimal number in *d, the length of that number in len,
+ * and a pointer to a character array to put the output (*out).
+ * Postcondition: A String representation of the given hexadecimal number is
+ * placed into the array *out
+ *
+ * 'out' MUST point to an array of at least len * 2 + 1
+ *
+ * Return values: ISC_R_SUCCESS if the operation is sucessful
+ */
+static isc_result_t
+tohexstr(unsigned char *d, unsigned int len, char *out, size_t out_size) {
+ char c_ret[] = "AA";
+ unsigned int j;
+
+ out[0] = '\0';
+ strlcat(out, "0x", out_size);
+ for (j = 0; j < len; j++) {
+ snprintf(c_ret, sizeof(c_ret), "%02X", d[j]);
+ strlcat(out, c_ret, out_size);
+ }
+ return (ISC_R_SUCCESS);
+}
+
+
+#define TEST_INPUT(x) (x), sizeof(x)-1
+
+typedef struct hash_testcase {
+ const char *input;
+ size_t input_len;
+ const char *result;
+ int repeats;
+} hash_testcase_t;
+
+ATF_TC(isc_gost_md);
+ATF_TC_HEAD(isc_gost_md, tc) {
+ atf_tc_set_md_var(tc, "descr",
+ "GOST R 34.11-94 examples from Wikipedia");
+}
+ATF_TC_BODY(isc_gost_md, tc) {
+ isc_gost_t gost;
+ isc_result_t result;
+
+ UNUSED(tc);
+
+ /*
+ * These are the various test vectors. All of these are passed
+ * through the hash function and the results are compared to the
+ * result specified here.
+ */
+ hash_testcase_t testcases[] = {
+ /* Test 1 */
+ {
+ TEST_INPUT(""),
+ "0x981E5F3CA30C841487830F84FB433E1"
+ "3AC1101569B9C13584AC483234CD656C0",
+ 1
+ },
+ /* Test 2 */
+ {
+ TEST_INPUT("a"),
+ "0xE74C52DD282183BF37AF0079C9F7805"
+ "5715A103F17E3133CEFF1AACF2F403011",
+ 1
+ },
+ /* Test 3 */
+ {
+ TEST_INPUT("abc"),
+ "0xB285056DBF18D7392D7677369524DD1"
+ "4747459ED8143997E163B2986F92FD42C",
+ 1
+ },
+ /* Test 4 */
+ {
+ TEST_INPUT("message digest"),
+ "0xBC6041DD2AA401EBFA6E9886734174F"
+ "EBDB4729AA972D60F549AC39B29721BA0",
+ 1
+ },
+ /* Test 5 */
+ {
+ TEST_INPUT("The quick brown fox jumps "
+ "over the lazy dog"),
+ "0x9004294A361A508C586FE53D1F1B027"
+ "46765E71B765472786E4770D565830A76",
+ 1
+ },
+
+ /* Test 6 */
+ {
+ TEST_INPUT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcde"
+ "fghijklmnopqrstuvwxyz0123456789"),
+ "0x73B70A39497DE53A6E08C67B6D4DB85"
+ "3540F03E9389299D9B0156EF7E85D0F61",
+ 1
+ },
+ /* Test 7 */
+ {
+ TEST_INPUT("1234567890123456789012345678901"
+ "2345678901234567890123456789012"
+ "345678901234567890"),
+ "0x6BC7B38989B28CF93AE8842BF9D7529"
+ "05910A7528A61E5BCE0782DE43E610C90",
+ 1
+ },
+ /* Test 8 */
+ {
+ TEST_INPUT("This is message, length=32 bytes"),
+ "0x2CEFC2F7B7BDC514E18EA57FA74FF35"
+ "7E7FA17D652C75F69CB1BE7893EDE48EB",
+ 1
+ },
+ /* Test 9 */
+ {
+ TEST_INPUT("Suppose the original message "
+ "has length = 50 bytes"),
+ "0xC3730C5CBCCACF915AC292676F21E8B"
+ "D4EF75331D9405E5F1A61DC3130A65011",
+ 1
+ },
+ /* Test 10 */
+ {
+ TEST_INPUT("U") /* times 128 */,
+ "0x1C4AC7614691BBF427FA2316216BE8F"
+ "10D92EDFD37CD1027514C1008F649C4E8",
+ 128
+ },
+ /* Test 11 */
+ {
+ TEST_INPUT("a") /* times 1000000 */,
+ "0x8693287AA62F9478F7CB312EC0866B6"
+ "C4E4A0F11160441E8F4FFCD2715DD554F",
+ 1000000
+ },
+ { NULL, 0, NULL, 1 }
+ };
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE(result == ISC_R_SUCCESS);
+
+ hash_testcase_t *testcase = testcases;
+
+ while (testcase->input != NULL && testcase->result != NULL) {
+ result = isc_gost_init(&gost);
+ ATF_REQUIRE(result == ISC_R_SUCCESS);
+ for(i = 0; i < testcase->repeats; i++) {
+ result = isc_gost_update(&gost,
+ (const uint8_t *) testcase->input,
+ testcase->input_len);
+ ATF_REQUIRE(result == ISC_R_SUCCESS);
+ }
+ result = isc_gost_final(&gost, digest);
+ ATF_REQUIRE(result == ISC_R_SUCCESS);
+ tohexstr(digest, ISC_GOST_DIGESTLENGTH, str, sizeof(str));
+ ATF_CHECK_STREQ(str, testcase->result);
+
+ testcase++;
+ }
+
+ dns_test_end();
+}
+
+ATF_TC(isc_gost_private);
+ATF_TC_HEAD(isc_gost_private, tc) {
+ atf_tc_set_md_var(tc, "descr", "GOST R 34.10-2001 private key");
+}
+ATF_TC_BODY(isc_gost_private, tc) {
+ isc_result_t result;
+ unsigned char privraw[31] = {
+ 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
+ };
+#ifdef HAVE_OPENSSL_GOST
+ unsigned char rbuf[32];
+ unsigned char privasn1[70] = {
+ 0x30, 0x44, 0x02, 0x01, 0x00, 0x30, 0x1c, 0x06,
+ 0x06, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x13, 0x30,
+ 0x12, 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02,
+ 0x23, 0x01, 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02,
+ 0x02, 0x1e, 0x01, 0x04, 0x21, 0x02, 0x1f, 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
+ };
+ unsigned char abuf[71];
+ unsigned char gost_dummy_key[71] = {
+ 0x30, 0x45, 0x02, 0x01, 0x00, 0x30, 0x1c, 0x06,
+ 0x06, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x13, 0x30,
+ 0x12, 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02,
+ 0x23, 0x01, 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02,
+ 0x02, 0x1e, 0x01, 0x04, 0x22, 0x02, 0x20, 0x1b,
+ 0x3f, 0x94, 0xf7, 0x1a, 0x5f, 0x2f, 0xe7, 0xe5,
+ 0x74, 0x0b, 0x8c, 0xd4, 0xb7, 0x18, 0xdd, 0x65,
+ 0x68, 0x26, 0xd1, 0x54, 0xfb, 0x77, 0xba, 0x63,
+ 0x72, 0xd9, 0xf0, 0x63, 0x87, 0xe0, 0xd6
+ };
+ EVP_PKEY *pkey;
+ EC_KEY *eckey;
+ BIGNUM *privkey;
+ const BIGNUM *privkey1;
+ const unsigned char *p;
+ int len;
+ unsigned char *q;
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE(result == ISC_R_SUCCESS);
+
+ /* raw parse */
+ privkey = BN_bin2bn(privraw, (int) sizeof(privraw), NULL);
+ ATF_REQUIRE(privkey != NULL);
+ p = gost_dummy_key;
+ pkey = NULL;
+ ATF_REQUIRE(d2i_PrivateKey(NID_id_GostR3410_2001, &pkey, &p,
+ (long) sizeof(gost_dummy_key)) != NULL);
+ ATF_REQUIRE(pkey != NULL);
+ ATF_REQUIRE(EVP_PKEY_bits(pkey) == 256);
+ eckey = EVP_PKEY_get0(pkey);
+ ATF_REQUIRE(eckey != NULL);
+ ATF_REQUIRE(EC_KEY_set_private_key(eckey, privkey) == 1);
+ BN_clear_free(privkey);
+
+ /* asn1 tofile */
+ len = i2d_PrivateKey(pkey, NULL);
+ ATF_REQUIRE(len == 70);
+ q = abuf;
+ ATF_REQUIRE(i2d_PrivateKey(pkey, &q) == len);
+ ATF_REQUIRE(memcmp(abuf, privasn1, len) == 0);
+ EVP_PKEY_free(pkey);
+
+ /* asn1 parse */
+ p = privasn1;
+ pkey = NULL;
+ ATF_REQUIRE(d2i_PrivateKey(NID_id_GostR3410_2001, &pkey, &p,
+ (long) len) != NULL);
+ ATF_REQUIRE(pkey != NULL);
+ eckey = EVP_PKEY_get0(pkey);
+ ATF_REQUIRE(eckey != NULL);
+ privkey1 = EC_KEY_get0_private_key(eckey);
+ len = BN_num_bytes(privkey1);
+ ATF_REQUIRE(len == 31);
+ ATF_REQUIRE(BN_bn2bin(privkey1, rbuf) == len);
+ ATF_REQUIRE(memcmp(rbuf, privraw, len) == 0);
+
+ dns_test_end();
+#else
+ CK_BBOOL truevalue = TRUE;
+ CK_BBOOL falsevalue = FALSE;
+ CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY;
+ CK_KEY_TYPE keyType = CKK_GOSTR3410;
+ CK_ATTRIBUTE keyTemplate[] =
+ {
+ { CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+ { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+ { CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_SENSITIVE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+ { CKA_SIGN, &truevalue, (CK_ULONG) sizeof(truevalue) },
+ { CKA_VALUE, privraw, sizeof(privraw) },
+ { CKA_GOSTR3410_PARAMS, pk11_gost_a_paramset,
+ (CK_ULONG) sizeof(pk11_gost_a_paramset) },
+ { CKA_GOSTR3411_PARAMS, pk11_gost_paramset,
+ (CK_ULONG) sizeof(pk11_gost_paramset) }
+ };
+ CK_MECHANISM mech = { CKM_GOSTR3410_WITH_GOSTR3411, NULL, 0 };
+ CK_BYTE sig[64];
+ CK_ULONG siglen;
+ pk11_context_t pk11_ctx;
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE(result == ISC_R_SUCCESS);
+
+ /* create the private key */
+ memset(&pk11_ctx, 0, sizeof(pk11_ctx));
+ ATF_REQUIRE(pk11_get_session(&pk11_ctx, OP_GOST, true,
+ false, false, NULL,
+ pk11_get_best_token(OP_GOST)) ==
+ ISC_R_SUCCESS);
+ pk11_ctx.object = CK_INVALID_HANDLE;
+ pk11_ctx.ontoken = false;
+ ATF_REQUIRE(pkcs_C_CreateObject(pk11_ctx.session, keyTemplate,
+ (CK_ULONG) 9, &pk11_ctx.object) ==
+ CKR_OK);
+ ATF_REQUIRE(pk11_ctx.object != CK_INVALID_HANDLE);
+
+ /* sign something */
+ ATF_REQUIRE(pkcs_C_SignInit(pk11_ctx.session, &mech,
+ pk11_ctx.object) == CKR_OK);
+ siglen = 0;
+ ATF_REQUIRE(pkcs_C_Sign(pk11_ctx.session, sig, 64,
+ NULL, &siglen) == CKR_OK);
+ ATF_REQUIRE(siglen == 64);
+ ATF_REQUIRE(pkcs_C_Sign(pk11_ctx.session, sig, 64,
+ sig, &siglen) == CKR_OK);
+ ATF_REQUIRE(siglen == 64);
+
+ dns_test_end();
+#endif
+};
+#else
+ATF_TC(untested);
+ATF_TC_HEAD(untested, tc) {
+ atf_tc_set_md_var(tc, "descr", "skipping gost test");
+}
+ATF_TC_BODY(untested, tc) {
+ UNUSED(tc);
+ atf_tc_skip("GOST not available");
+}
+#endif
+/*
+ * Main
+ */
+ATF_TP_ADD_TCS(tp) {
+#if defined(HAVE_OPENSSL_GOST) || defined(HAVE_PKCS11_GOST)
+ ATF_TP_ADD_TC(tp, isc_gost_md);
+ ATF_TP_ADD_TC(tp, isc_gost_private);
+#else
+ ATF_TP_ADD_TC(tp, untested);
+#endif
+ return (atf_no_error());
+}
+
diff --git a/lib/dns/tests/keytable_test.c b/lib/dns/tests/keytable_test.c
new file mode 100644
index 0000000..ed69fcc
--- /dev/null
+++ b/lib/dns/tests/keytable_test.c
@@ -0,0 +1,622 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <atf-c.h>
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <inttypes.h>
+#include <unistd.h>
+
+#if defined(OPENSSL) || defined(PKCS11CRYPTO)
+
+#include <isc/base64.h>
+#include <isc/buffer.h>
+#include <isc/util.h>
+
+#include <dns/name.h>
+#include <dns/fixedname.h>
+#include <dns/keytable.h>
+#include <dns/nta.h>
+#include <dns/rdataclass.h>
+#include <dns/rdatastruct.h>
+#include <dns/rootns.h>
+#include <dns/view.h>
+
+#include <dst/dst.h>
+
+#include "dnstest.h"
+
+dns_keytable_t *keytable = NULL;
+dns_ntatable_t *ntatable = NULL;
+
+static const char *keystr1 = "BQEAAAABok+vaUC9neRv8yeT/FEGgN7svR8s7VBUVSBd8NsAiV8AlaAg O5FHar3JQd95i/puZos6Vi6at9/JBbN8qVmO2AuiXxVqfxMKxIcy+LEB 0Vw4NaSJ3N3uaVREso6aTSs98H/25MjcwLOr7SFfXA7bGhZatLtYY/xu kp6Km5hMfkE=";
+static const dns_keytag_t keytag1 = 30591;
+
+static const char *keystr2 = "BQEAAAABwuHz9Cem0BJ0JQTO7C/a3McR6hMaufljs1dfG/inaJpYv7vH XTrAOm/MeKp+/x6eT4QLru0KoZkvZJnqTI8JyaFTw2OM/ItBfh/hL2lm Cft2O7n3MfeqYtvjPnY7dWghYW4sVfH7VVEGm958o9nfi79532Qeklxh x8pXWdeAaRU=";
+
+static dns_view_t *view = NULL;
+
+/*
+ * Test utilities. In general, these assume input parameters are valid
+ * (checking with ATF_REQUIRE_EQ, thus aborting if not) and unlikely run time
+ * errors (such as memory allocation failure) won't happen. This helps keep
+ * the test code concise.
+ */
+
+/*
+ * Utility to convert C-string to dns_name_t. Return a pointer to
+ * static data, and so is not thread safe.
+ */
+static dns_name_t *
+str2name(const char *namestr) {
+ static dns_fixedname_t fname;
+ static dns_name_t *name;
+ static isc_buffer_t namebuf;
+ void *deconst_namestr;
+
+ name = dns_fixedname_initname(&fname);
+ DE_CONST(namestr, deconst_namestr); /* OK, since we don't modify it */
+ isc_buffer_init(&namebuf, deconst_namestr, strlen(deconst_namestr));
+ isc_buffer_add(&namebuf, strlen(namestr));
+ ATF_REQUIRE_EQ(dns_name_fromtext(name, &namebuf, dns_rootname, 0,
+ NULL), ISC_R_SUCCESS);
+
+ return (name);
+}
+
+static void
+create_key(uint16_t flags, uint8_t proto, uint8_t alg,
+ const char *keynamestr, const char *keystr, dst_key_t **target)
+{
+ dns_rdata_dnskey_t keystruct;
+ unsigned char keydata[4096];
+ isc_buffer_t keydatabuf;
+ unsigned char rrdata[4096];
+ isc_buffer_t rrdatabuf;
+ isc_region_t r;
+ const dns_rdataclass_t rdclass = dns_rdataclass_in; /* for brevity */
+
+ keystruct.common.rdclass = rdclass;
+ keystruct.common.rdtype = dns_rdatatype_dnskey;
+ keystruct.mctx = NULL;
+ ISC_LINK_INIT(&keystruct.common, link);
+ keystruct.flags = flags;
+ keystruct.protocol = proto;
+ keystruct.algorithm = alg;
+
+ isc_buffer_init(&keydatabuf, keydata, sizeof(keydata));
+ isc_buffer_init(&rrdatabuf, rrdata, sizeof(rrdata));
+ ATF_REQUIRE_EQ(isc_base64_decodestring(keystr, &keydatabuf),
+ ISC_R_SUCCESS);
+ isc_buffer_usedregion(&keydatabuf, &r);
+ keystruct.datalen = r.length;
+ keystruct.data = r.base;
+ ATF_REQUIRE_EQ(dns_rdata_fromstruct(NULL, keystruct.common.rdclass,
+ keystruct.common.rdtype,
+ &keystruct, &rrdatabuf),
+ ISC_R_SUCCESS);
+
+ ATF_REQUIRE_EQ(dst_key_fromdns(str2name(keynamestr), rdclass,
+ &rrdatabuf, mctx, target),
+ ISC_R_SUCCESS);
+}
+
+/* Common setup: create a keytable and ntatable to test with a few keys */
+static void
+create_tables() {
+ isc_result_t result;
+ dst_key_t *key = NULL;
+ isc_stdtime_t now;
+
+ result = dns_test_makeview("view", &view);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ ATF_REQUIRE_EQ(dns_keytable_create(mctx, &keytable), ISC_R_SUCCESS);
+ ATF_REQUIRE_EQ(dns_ntatable_create(view, taskmgr, timermgr,
+ &ntatable), ISC_R_SUCCESS);
+
+ /* Add a normal key */
+ create_key(257, 3, 5, "example.com", keystr1, &key);
+ ATF_REQUIRE_EQ(dns_keytable_add(keytable, false, &key),
+ ISC_R_SUCCESS);
+
+ /* Add a null key */
+ ATF_REQUIRE_EQ(dns_keytable_marksecure(keytable,
+ str2name("null.example")),
+ ISC_R_SUCCESS);
+
+ /* Add a negative trust anchor, duration 1 hour */
+ isc_stdtime_get(&now);
+ ATF_REQUIRE_EQ(dns_ntatable_add(ntatable,
+ str2name("insecure.example"),
+ false, now, 3600),
+ ISC_R_SUCCESS);
+}
+
+static void
+destroy_tables() {
+ if (ntatable != NULL)
+ dns_ntatable_detach(&ntatable);
+ if (keytable != NULL)
+ dns_keytable_detach(&keytable);
+
+ dns_view_detach(&view);
+}
+
+/*
+ * Individual unit tests
+ */
+
+ATF_TC(add);
+ATF_TC_HEAD(add, tc) {
+ atf_tc_set_md_var(tc, "descr", "add keys to the keytable");
+}
+ATF_TC_BODY(add, tc) {
+ dst_key_t *key = NULL;
+ dns_keynode_t *keynode = NULL;
+ dns_keynode_t *next_keynode = NULL;
+ dns_keynode_t *null_keynode = NULL;
+
+ UNUSED(tc);
+
+ ATF_REQUIRE_EQ(dns_test_begin(NULL, true), ISC_R_SUCCESS);
+ create_tables();
+
+ /*
+ * Get the keynode for the example.com key. There's no other key for
+ * the name, so nextkeynode() should return NOTFOUND.
+ */
+ ATF_REQUIRE_EQ(dns_keytable_find(keytable, str2name("example.com"),
+ &keynode), ISC_R_SUCCESS);
+ ATF_REQUIRE_EQ(dns_keytable_nextkeynode(keytable, keynode,
+ &next_keynode), ISC_R_NOTFOUND);
+
+ /*
+ * Try to add the same key. This should have no effect, so
+ * nextkeynode() should still return NOTFOUND.
+ */
+ create_key(257, 3, 5, "example.com", keystr1, &key);
+ ATF_REQUIRE_EQ(dns_keytable_add(keytable, false, &key),
+ ISC_R_SUCCESS);
+ ATF_REQUIRE_EQ(dns_keytable_nextkeynode(keytable, keynode,
+ &next_keynode), ISC_R_NOTFOUND);
+
+ /* Add another key (different keydata) */
+ dns_keytable_detachkeynode(keytable, &keynode);
+ create_key(257, 3, 5, "example.com", keystr2, &key);
+ ATF_REQUIRE_EQ(dns_keytable_add(keytable, false, &key),
+ ISC_R_SUCCESS);
+ ATF_REQUIRE_EQ(dns_keytable_find(keytable, str2name("example.com"),
+ &keynode), ISC_R_SUCCESS);
+ ATF_REQUIRE_EQ(dns_keytable_nextkeynode(keytable, keynode,
+ &next_keynode), ISC_R_SUCCESS);
+ dns_keytable_detachkeynode(keytable, &next_keynode);
+
+ /*
+ * Add a normal key to a name that has a null key. The null key node
+ * will be updated with the normal key.
+ */
+ dns_keytable_detachkeynode(keytable, &keynode);
+ ATF_REQUIRE_EQ(dns_keytable_find(keytable, str2name("null.example"),
+ &null_keynode), ISC_R_SUCCESS);
+ create_key(257, 3, 5, "null.example", keystr2, &key);
+ ATF_REQUIRE_EQ(dns_keytable_add(keytable, false, &key),
+ ISC_R_SUCCESS);
+ ATF_REQUIRE_EQ(dns_keytable_find(keytable, str2name("null.example"),
+ &keynode), ISC_R_SUCCESS);
+ ATF_REQUIRE_EQ(keynode, null_keynode); /* should be the same node */
+ ATF_REQUIRE(dns_keynode_key(keynode) != NULL); /* now have a key */
+ dns_keytable_detachkeynode(keytable, &null_keynode);
+
+ /*
+ * Try to add a null key to a name that already has a key. It's
+ * effectively no-op, so the same key node is still there, with no
+ * no next node.
+ * (Note: this and above checks confirm that if a name has a null key
+ * that's the only key for the name).
+ */
+ ATF_REQUIRE_EQ(dns_keytable_marksecure(keytable,
+ str2name("null.example")),
+ ISC_R_SUCCESS);
+ ATF_REQUIRE_EQ(dns_keytable_find(keytable, str2name("null.example"),
+ &null_keynode), ISC_R_SUCCESS);
+ ATF_REQUIRE_EQ(keynode, null_keynode);
+ ATF_REQUIRE(dns_keynode_key(keynode) != NULL);
+ ATF_REQUIRE_EQ(dns_keytable_nextkeynode(keytable, keynode,
+ &next_keynode), ISC_R_NOTFOUND);
+ dns_keytable_detachkeynode(keytable, &null_keynode);
+
+ dns_keytable_detachkeynode(keytable, &keynode);
+ destroy_tables();
+ dns_test_end();
+}
+
+ATF_TC(delete);
+ATF_TC_HEAD(delete, tc) {
+ atf_tc_set_md_var(tc, "descr", "delete keys from the keytable");
+}
+ATF_TC_BODY(delete, tc) {
+ UNUSED(tc);
+
+ ATF_REQUIRE_EQ(dns_test_begin(NULL, true), ISC_R_SUCCESS);
+ create_tables();
+
+ /* dns_keytable_delete requires exact match */
+ ATF_REQUIRE_EQ(dns_keytable_delete(keytable, str2name("example.org")),
+ ISC_R_NOTFOUND);
+ ATF_REQUIRE_EQ(dns_keytable_delete(keytable, str2name("s.example.com")),
+ ISC_R_NOTFOUND);
+ ATF_REQUIRE_EQ(dns_keytable_delete(keytable, str2name("example.com")),
+ ISC_R_SUCCESS);
+
+ /* works also for nodes with a null key */
+ ATF_REQUIRE_EQ(dns_keytable_delete(keytable, str2name("null.example")),
+ ISC_R_SUCCESS);
+
+ /* or a negative trust anchor */
+ ATF_REQUIRE_EQ(dns_ntatable_delete(ntatable,
+ str2name("insecure.example")),
+ ISC_R_SUCCESS);
+
+ destroy_tables();
+ dns_test_end();
+}
+
+ATF_TC(deletekeynode);
+ATF_TC_HEAD(deletekeynode, tc) {
+ atf_tc_set_md_var(tc, "descr", "delete key nodes from the keytable");
+}
+ATF_TC_BODY(deletekeynode, tc) {
+ dst_key_t *key = NULL;
+
+ UNUSED(tc);
+
+ ATF_REQUIRE_EQ(dns_test_begin(NULL, true), ISC_R_SUCCESS);
+ create_tables();
+
+ /* key name doesn't match */
+ create_key(257, 3, 5, "example.org", keystr1, &key);
+ ATF_REQUIRE_EQ(dns_keytable_deletekeynode(keytable, key),
+ ISC_R_NOTFOUND);
+ dst_key_free(&key);
+
+ /* subdomain match is the same as no match */
+ create_key(257, 3, 5, "sub.example.com", keystr1, &key);
+ ATF_REQUIRE_EQ(dns_keytable_deletekeynode(keytable, key),
+ ISC_R_NOTFOUND);
+ dst_key_free(&key);
+
+ /* name matches but key doesn't match (resulting in PARTIALMATCH) */
+ create_key(257, 3, 5, "example.com", keystr2, &key);
+ ATF_REQUIRE_EQ(dns_keytable_deletekeynode(keytable, key),
+ DNS_R_PARTIALMATCH);
+ dst_key_free(&key);
+
+ /*
+ * exact match. after deleting the node the internal rbt node will be
+ * empty, and any delete or deletekeynode attempt should result in
+ * NOTFOUND.
+ */
+ create_key(257, 3, 5, "example.com", keystr1, &key);
+ ATF_REQUIRE_EQ(dns_keytable_deletekeynode(keytable, key),
+ ISC_R_SUCCESS);
+ ATF_REQUIRE_EQ(dns_keytable_deletekeynode(keytable, key),
+ ISC_R_NOTFOUND);
+ ATF_REQUIRE_EQ(dns_keytable_delete(keytable, str2name("example.com")),
+ ISC_R_NOTFOUND);
+ dst_key_free(&key);
+
+ /*
+ * A null key node for a name is not deleted when searched by key;
+ * it must be deleted by dns_keytable_delete()
+ */
+ create_key(257, 3, 5, "null.example", keystr1, &key);
+ ATF_REQUIRE_EQ(dns_keytable_deletekeynode(keytable, key),
+ DNS_R_PARTIALMATCH);
+ ATF_REQUIRE_EQ(dns_keytable_delete(keytable, dst_key_name(key)),
+ ISC_R_SUCCESS);
+ dst_key_free(&key);
+
+ destroy_tables();
+ dns_test_end();
+}
+
+ATF_TC(find);
+ATF_TC_HEAD(find, tc) {
+ atf_tc_set_md_var(tc, "descr", "check find-variant operations");
+}
+ATF_TC_BODY(find, tc) {
+ dns_keynode_t *keynode = NULL;
+ dns_fixedname_t fname;
+ dns_name_t *name;
+
+ UNUSED(tc);
+
+ ATF_REQUIRE_EQ(dns_test_begin(NULL, true), ISC_R_SUCCESS);
+ create_tables();
+
+ /*
+ * dns_keytable_find() requires exact name match. It matches node
+ * that has a null key, too.
+ */
+ ATF_REQUIRE_EQ(dns_keytable_find(keytable, str2name("example.org"),
+ &keynode), ISC_R_NOTFOUND);
+ ATF_REQUIRE_EQ(dns_keytable_find(keytable, str2name("sub.example.com"),
+ &keynode), ISC_R_NOTFOUND);
+ ATF_REQUIRE_EQ(dns_keytable_find(keytable, str2name("example.com"),
+ &keynode), ISC_R_SUCCESS);
+ dns_keytable_detachkeynode(keytable, &keynode);
+ ATF_REQUIRE_EQ(dns_keytable_find(keytable, str2name("null.example"),
+ &keynode), ISC_R_SUCCESS);
+ ATF_REQUIRE_EQ(dns_keynode_key(keynode), NULL);
+ dns_keytable_detachkeynode(keytable, &keynode);
+
+ /*
+ * dns_keytable_finddeepestmatch() allows partial match. Also match
+ * nodes with a null key.
+ */
+ name = dns_fixedname_initname(&fname);
+ ATF_REQUIRE_EQ(dns_keytable_finddeepestmatch(keytable,
+ str2name("example.com"),
+ name), ISC_R_SUCCESS);
+ ATF_REQUIRE_EQ(dns_name_equal(name, str2name("example.com")), true);
+ ATF_REQUIRE_EQ(dns_keytable_finddeepestmatch(keytable,
+ str2name("s.example.com"),
+ name), ISC_R_SUCCESS);
+ ATF_REQUIRE_EQ(dns_name_equal(name, str2name("example.com")), true);
+ ATF_REQUIRE_EQ(dns_keytable_finddeepestmatch(keytable,
+ str2name("example.org"),
+ name), ISC_R_NOTFOUND);
+ ATF_REQUIRE_EQ(dns_keytable_finddeepestmatch(keytable,
+ str2name("null.example"),
+ name), ISC_R_SUCCESS);
+ ATF_REQUIRE_EQ(dns_name_equal(name, str2name("null.example")),
+ true);
+
+ /*
+ * dns_keytable_findkeynode() requires exact name, algorithm, keytag
+ * match. If algorithm or keytag doesn't match, should result in
+ * PARTIALMATCH. Same for a node with a null key.
+ */
+ ATF_REQUIRE_EQ(dns_keytable_findkeynode(keytable,
+ str2name("example.org"),
+ 5, keytag1, &keynode),
+ ISC_R_NOTFOUND);
+ ATF_REQUIRE_EQ(dns_keytable_findkeynode(keytable,
+ str2name("sub.example.com"),
+ 5, keytag1, &keynode),
+ ISC_R_NOTFOUND);
+ ATF_REQUIRE_EQ(dns_keytable_findkeynode(keytable,
+ str2name("example.com"),
+ 4, keytag1, &keynode),
+ DNS_R_PARTIALMATCH); /* different algorithm */
+ ATF_REQUIRE_EQ(dns_keytable_findkeynode(keytable,
+ str2name("example.com"),
+ 5, keytag1 + 1, &keynode),
+ DNS_R_PARTIALMATCH); /* different keytag */
+ ATF_REQUIRE_EQ(dns_keytable_findkeynode(keytable,
+ str2name("null.example"),
+ 5, 0, &keynode),
+ DNS_R_PARTIALMATCH); /* null key */
+ ATF_REQUIRE_EQ(dns_keytable_findkeynode(keytable,
+ str2name("example.com"),
+ 5, keytag1, &keynode),
+ ISC_R_SUCCESS); /* complete match */
+ dns_keytable_detachkeynode(keytable, &keynode);
+
+ destroy_tables();
+ dns_test_end();
+}
+
+ATF_TC(issecuredomain);
+ATF_TC_HEAD(issecuredomain, tc) {
+ atf_tc_set_md_var(tc, "descr", "check issecuredomain()");
+}
+ATF_TC_BODY(issecuredomain, tc) {
+ bool issecure;
+ const char **n;
+ const char *names[] = {"example.com", "sub.example.com",
+ "null.example", "sub.null.example", NULL};
+
+ UNUSED(tc);
+ ATF_REQUIRE_EQ(dns_test_begin(NULL, true), ISC_R_SUCCESS);
+ create_tables();
+
+ /*
+ * Domains that are an exact or partial match of a key name are
+ * considered secure. It's the case even if the key is null
+ * (validation will then fail, but that's actually the intended effect
+ * of installing a null key).
+ */
+ for (n = names; *n != NULL; n++) {
+ ATF_REQUIRE_EQ(dns_keytable_issecuredomain(keytable,
+ str2name(*n),
+ NULL,
+ &issecure),
+ ISC_R_SUCCESS);
+ ATF_REQUIRE_EQ(issecure, true);
+ }
+
+ /*
+ * If the key table has no entry (not even a null one) for a domain or
+ * any of its ancestors, that domain is considered insecure.
+ */
+ ATF_REQUIRE_EQ(dns_keytable_issecuredomain(keytable,
+ str2name("example.org"),
+ NULL,
+ &issecure),
+ ISC_R_SUCCESS);
+ ATF_REQUIRE_EQ(issecure, false);
+
+ destroy_tables();
+ dns_test_end();
+}
+
+ATF_TC(dump);
+ATF_TC_HEAD(dump, tc) {
+ atf_tc_set_md_var(tc, "descr", "check dns_keytable_dump()");
+}
+ATF_TC_BODY(dump, tc) {
+ UNUSED(tc);
+
+ ATF_REQUIRE_EQ(dns_test_begin(NULL, true), ISC_R_SUCCESS);
+ create_tables();
+
+ /*
+ * Right now, we only confirm the dump attempt doesn't cause disruption
+ * (so we don't check the dump content).
+ */
+ ATF_REQUIRE_EQ(dns_keytable_dump(keytable, stdout), ISC_R_SUCCESS);
+
+ destroy_tables();
+ dns_test_end();
+}
+
+ATF_TC(nta);
+ATF_TC_HEAD(nta, tc) {
+ atf_tc_set_md_var(tc, "descr", "check negative trust anchors");
+}
+ATF_TC_BODY(nta, tc) {
+ isc_result_t result;
+ dst_key_t *key = NULL;
+ bool issecure, covered;
+ dns_view_t *myview = NULL;
+ isc_stdtime_t now;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, true);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_test_makeview("view", &myview);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = isc_task_create(taskmgr, 0, &myview->task);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_view_initsecroots(myview, mctx);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ result = dns_view_getsecroots(myview, &keytable);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_view_initntatable(myview, taskmgr, timermgr);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ result = dns_view_getntatable(myview, &ntatable);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ create_key(257, 3, 5, "example", keystr1, &key);
+ result = dns_keytable_add(keytable, false, &key);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ isc_stdtime_get(&now);
+ result = dns_ntatable_add(ntatable,
+ str2name("insecure.example"),
+ false, now, 1);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ /* Should be secure */
+ result = dns_view_issecuredomain(myview,
+ str2name("test.secure.example"),
+ now, true, &issecure);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ ATF_CHECK(issecure);
+
+ /* Should not be secure */
+ result = dns_view_issecuredomain(myview,
+ str2name("test.insecure.example"),
+ now, true, &issecure);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ ATF_CHECK(!issecure);
+
+ /* NTA covered */
+ covered = dns_view_ntacovers(myview, now, str2name("insecure.example"),
+ dns_rootname);
+ ATF_CHECK(covered);
+
+ /* Not NTA covered */
+ covered = dns_view_ntacovers(myview, now, str2name("secure.example"),
+ dns_rootname);
+ ATF_CHECK(!covered);
+
+ /* As of now + 2, the NTA should be clear */
+ result = dns_view_issecuredomain(myview,
+ str2name("test.insecure.example"),
+ now + 2, true, &issecure);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ ATF_CHECK(issecure);
+
+ /* Now check deletion */
+ result = dns_view_issecuredomain(myview, str2name("test.new.example"),
+ now, true, &issecure);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ ATF_CHECK(issecure);
+
+ result = dns_ntatable_add(ntatable, str2name("new.example"),
+ false, now, 3600);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_view_issecuredomain(myview, str2name("test.new.example"),
+ now, true, &issecure);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ ATF_CHECK(!issecure);
+
+ result = dns_ntatable_delete(ntatable, str2name("new.example"));
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_view_issecuredomain(myview, str2name("test.new.example"),
+ now, true, &issecure);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ ATF_CHECK(issecure);
+
+ /* Clean up */
+ dns_ntatable_detach(&ntatable);
+ dns_keytable_detach(&keytable);
+ dns_view_detach(&myview);
+
+ dns_test_end();
+}
+
+#else
+#include <isc/util.h>
+
+ATF_TC(untested);
+ATF_TC_HEAD(untested, tc) {
+ atf_tc_set_md_var(tc, "descr", "skipping keytable test");
+}
+ATF_TC_BODY(untested, tc) {
+ UNUSED(tc);
+ atf_tc_skip("DNSSEC not available");
+}
+#endif
+
+/*
+ * Main
+ */
+ATF_TP_ADD_TCS(tp) {
+#if defined(OPENSSL) || defined(PKCS11CRYPTO)
+ ATF_TP_ADD_TC(tp, add);
+ ATF_TP_ADD_TC(tp, delete);
+ ATF_TP_ADD_TC(tp, deletekeynode);
+ ATF_TP_ADD_TC(tp, find);
+ ATF_TP_ADD_TC(tp, issecuredomain);
+ ATF_TP_ADD_TC(tp, dump);
+ ATF_TP_ADD_TC(tp, nta);
+#else
+ ATF_TP_ADD_TC(tp, untested);
+#endif
+
+ return (atf_no_error());
+}
diff --git a/lib/dns/tests/master_test.c b/lib/dns/tests/master_test.c
new file mode 100644
index 0000000..5cce2f3
--- /dev/null
+++ b/lib/dns/tests/master_test.c
@@ -0,0 +1,684 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <atf-c.h>
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <isc/print.h>
+#include <isc/string.h>
+#include <isc/xml.h>
+
+#include <dns/cache.h>
+#include <dns/callbacks.h>
+#include <dns/db.h>
+#include <dns/master.h>
+#include <dns/masterdump.h>
+#include <dns/name.h>
+#include <dns/rdata.h>
+#include <dns/rdatalist.h>
+#include <dns/rdataset.h>
+
+#include "dnstest.h"
+
+/*
+ * Helper functions
+ */
+
+#define BUFLEN 255
+#define BIGBUFLEN (70 * 1024)
+#define TEST_ORIGIN "test"
+
+static dns_masterrawheader_t header;
+static bool headerset;
+
+dns_name_t dns_origin;
+char origin[sizeof(TEST_ORIGIN)];
+unsigned char name_buf[BUFLEN];
+dns_rdatacallbacks_t callbacks;
+char *include_file = NULL;
+
+static isc_result_t
+add_callback(void *arg, dns_name_t *owner, dns_rdataset_t *dataset);
+
+static void
+rawdata_callback(dns_zone_t *zone, dns_masterrawheader_t *header);
+
+static isc_result_t
+add_callback(void *arg, dns_name_t *owner, dns_rdataset_t *dataset) {
+ char buf[BIGBUFLEN];
+ isc_buffer_t target;
+ isc_result_t result;
+
+ UNUSED(arg);
+
+ isc_buffer_init(&target, buf, BIGBUFLEN);
+ result = dns_rdataset_totext(dataset, owner, false, false,
+ &target);
+ return(result);
+}
+
+static void
+rawdata_callback(dns_zone_t *zone, dns_masterrawheader_t *h) {
+ UNUSED(zone);
+ header = *h;
+ headerset = true;
+}
+
+static isc_result_t
+setup_master(void (*warn)(struct dns_rdatacallbacks *, const char *, ...),
+ void (*error)(struct dns_rdatacallbacks *, const char *, ...))
+{
+ isc_result_t result;
+ int len;
+ isc_buffer_t source;
+ isc_buffer_t target;
+
+ strlcpy(origin, TEST_ORIGIN, sizeof(origin));
+ len = strlen(origin);
+ isc_buffer_init(&source, origin, len);
+ isc_buffer_add(&source, len);
+ isc_buffer_setactive(&source, len);
+ isc_buffer_init(&target, name_buf, BUFLEN);
+ dns_name_init(&dns_origin, NULL);
+ dns_master_initrawheader(&header);
+
+ result = dns_name_fromtext(&dns_origin, &source, dns_rootname,
+ 0, &target);
+ if (result != ISC_R_SUCCESS)
+ return(result);
+
+ dns_rdatacallbacks_init_stdio(&callbacks);
+ callbacks.add = add_callback;
+ callbacks.rawdata = rawdata_callback;
+ callbacks.zone = NULL;
+ if (warn != NULL)
+ callbacks.warn = warn;
+ if (error != NULL)
+ callbacks.error = error;
+ headerset = false;
+ return (result);
+}
+
+static isc_result_t
+test_master(const char *testfile, dns_masterformat_t format,
+ void (*warn)(struct dns_rdatacallbacks *, const char *, ...),
+ void (*error)(struct dns_rdatacallbacks *, const char *, ...))
+{
+ isc_result_t result;
+
+ result = setup_master(warn, error);
+ if (result != ISC_R_SUCCESS)
+ return(result);
+
+ result = dns_master_loadfile2(testfile, &dns_origin, &dns_origin,
+ dns_rdataclass_in, true,
+ &callbacks, mctx, format);
+ return (result);
+}
+
+static void
+include_callback(const char *filename, void *arg) {
+ char **argp = (char **) arg;
+ *argp = isc_mem_strdup(mctx, filename);
+}
+
+/*
+ * Individual unit tests
+ */
+
+/* Successful load test */
+ATF_TC(load);
+ATF_TC_HEAD(load, tc) {
+ atf_tc_set_md_var(tc, "descr", "dns_master_loadfile() loads a "
+ "valid master file and returns success");
+}
+ATF_TC_BODY(load, tc) {
+ isc_result_t result;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = test_master("testdata/master/master1.data",
+ dns_masterformat_text, NULL, NULL);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ dns_test_end();
+}
+
+
+/* Unepxected end of file test */
+ATF_TC(unexpected);
+ATF_TC_HEAD(unexpected, tc) {
+ atf_tc_set_md_var(tc, "descr", "dns_master_loadfile() returns "
+ "DNS_R_UNEXPECTED when file ends "
+ "too soon");
+}
+ATF_TC_BODY(unexpected, tc) {
+ isc_result_t result;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = test_master("testdata/master/master2.data",
+ dns_masterformat_text, NULL, NULL);
+ ATF_REQUIRE_EQ(result, ISC_R_UNEXPECTEDEND);
+
+ dns_test_end();
+}
+
+
+/* No owner test */
+ATF_TC(noowner);
+ATF_TC_HEAD(noowner, tc) {
+ atf_tc_set_md_var(tc, "descr", "dns_master_loadfile() accepts broken "
+ "zones with no TTL for first record "
+ "if it is an SOA");
+}
+ATF_TC_BODY(noowner, tc) {
+ isc_result_t result;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = test_master("testdata/master/master3.data",
+ dns_masterformat_text, NULL, NULL);
+ ATF_REQUIRE_EQ(result, DNS_R_NOOWNER);
+
+ dns_test_end();
+}
+
+
+/* No TTL test */
+ATF_TC(nottl);
+ATF_TC_HEAD(nottl, tc) {
+ atf_tc_set_md_var(tc, "descr", "dns_master_loadfile() returns "
+ "DNS_R_NOOWNER when no owner name "
+ "is specified");
+}
+
+ATF_TC_BODY(nottl, tc) {
+ isc_result_t result;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = test_master("testdata/master/master4.data",
+ dns_masterformat_text, NULL, NULL);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ dns_test_end();
+}
+
+
+/* Bad class test */
+ATF_TC(badclass);
+ATF_TC_HEAD(badclass, tc) {
+ atf_tc_set_md_var(tc, "descr", "dns_master_loadfile() returns "
+ "DNS_R_BADCLASS when record class "
+ "doesn't match zone class");
+}
+ATF_TC_BODY(badclass, tc) {
+ isc_result_t result;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = test_master("testdata/master/master5.data",
+ dns_masterformat_text, NULL, NULL);
+ ATF_REQUIRE_EQ(result, DNS_R_BADCLASS);
+
+ dns_test_end();
+}
+
+/* Too big rdata test */
+ATF_TC(toobig);
+ATF_TC_HEAD(toobig, tc) {
+ atf_tc_set_md_var(tc, "descr", "dns_master_loadfile() returns "
+ "ISC_R_NOSPACE when record is too big");
+}
+ATF_TC_BODY(toobig, tc) {
+ isc_result_t result;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = test_master("testdata/master/master15.data",
+ dns_masterformat_text, NULL, NULL);
+ ATF_REQUIRE_EQ(result, ISC_R_NOSPACE);
+
+ dns_test_end();
+}
+
+/* Maximum rdata test */
+ATF_TC(maxrdata);
+ATF_TC_HEAD(maxrdata, tc) {
+ atf_tc_set_md_var(tc, "descr", "dns_master_loadfile() returns "
+ "ISC_R_SUCCESS when record is maximum "
+ "size");
+}
+ATF_TC_BODY(maxrdata, tc) {
+ isc_result_t result;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = test_master("testdata/master/master16.data",
+ dns_masterformat_text, NULL, NULL);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ dns_test_end();
+}
+
+/* DNSKEY test */
+ATF_TC(dnskey);
+ATF_TC_HEAD(dnskey, tc) {
+ atf_tc_set_md_var(tc, "descr", "dns_master_loadfile() understands "
+ "DNSKEY with key material");
+}
+ATF_TC_BODY(dnskey, tc) {
+ isc_result_t result;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = test_master("testdata/master/master6.data",
+ dns_masterformat_text, NULL, NULL);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ dns_test_end();
+}
+
+
+/* DNSKEY with no key material test */
+ATF_TC(dnsnokey);
+ATF_TC_HEAD(dnsnokey, tc) {
+ atf_tc_set_md_var(tc, "descr", "dns_master_loadfile() understands "
+ "DNSKEY with no key material");
+}
+ATF_TC_BODY(dnsnokey, tc) {
+ isc_result_t result;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = test_master("testdata/master/master7.data",
+ dns_masterformat_text, NULL, NULL);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ dns_test_end();
+}
+
+/* Include test */
+ATF_TC(include);
+ATF_TC_HEAD(include, tc) {
+ atf_tc_set_md_var(tc, "descr", "dns_master_loadfile() understands "
+ "$INCLUDE");
+}
+ATF_TC_BODY(include, tc) {
+ isc_result_t result;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = test_master("testdata/master/master8.data",
+ dns_masterformat_text, NULL, NULL);
+ ATF_REQUIRE_EQ(result, DNS_R_SEENINCLUDE);
+
+ dns_test_end();
+}
+
+/* Include file list test */
+ATF_TC(master_includelist);
+ATF_TC_HEAD(master_includelist, tc) {
+ atf_tc_set_md_var(tc, "descr", "dns_master_loadfile4() returns "
+ "names of included file");
+}
+ATF_TC_BODY(master_includelist, tc) {
+ isc_result_t result;
+ char *filename = NULL;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = setup_master(NULL, NULL);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_master_loadfile4("testdata/master/master8.data",
+ &dns_origin, &dns_origin,
+ dns_rdataclass_in, 0, true,
+ &callbacks, include_callback,
+ &filename, mctx, dns_masterformat_text);
+ ATF_CHECK_EQ(result, DNS_R_SEENINCLUDE);
+ ATF_CHECK(filename != NULL);
+ if (filename != NULL) {
+ ATF_CHECK_STREQ(filename, "testdata/master/master7.data");
+ isc_mem_free(mctx, filename);
+ }
+
+ dns_test_end();
+}
+
+/* Include failure test */
+ATF_TC(includefail);
+ATF_TC_HEAD(includefail, tc) {
+ atf_tc_set_md_var(tc, "descr", "dns_master_loadfile() understands "
+ "$INCLUDE failures");
+}
+ATF_TC_BODY(includefail, tc) {
+ isc_result_t result;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = test_master("testdata/master/master9.data",
+ dns_masterformat_text, NULL, NULL);
+ ATF_REQUIRE_EQ(result, DNS_R_BADCLASS);
+
+ dns_test_end();
+}
+
+
+/* Non-empty blank lines test */
+ATF_TC(blanklines);
+ATF_TC_HEAD(blanklines, tc) {
+ atf_tc_set_md_var(tc, "descr", "dns_master_loadfile() handles "
+ "non-empty blank lines");
+}
+ATF_TC_BODY(blanklines, tc) {
+ isc_result_t result;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = test_master("testdata/master/master10.data",
+ dns_masterformat_text, NULL, NULL);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ dns_test_end();
+}
+
+/* SOA leading zeroes test */
+ATF_TC(leadingzero);
+ATF_TC_HEAD(leadingzero, tc) {
+ atf_tc_set_md_var(tc, "descr", "dns_master_loadfile() allows "
+ "leading zeroes in SOA");
+}
+ATF_TC_BODY(leadingzero, tc) {
+ isc_result_t result;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = test_master("testdata/master/master11.data",
+ dns_masterformat_text, NULL, NULL);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ dns_test_end();
+}
+
+ATF_TC(totext);
+ATF_TC_HEAD(totext, tc) {
+ atf_tc_set_md_var(tc, "descr", "masterfile totext tests");
+}
+ATF_TC_BODY(totext, tc) {
+ isc_result_t result;
+ dns_rdataset_t rdataset;
+ dns_rdatalist_t rdatalist;
+ isc_buffer_t target;
+ unsigned char buf[BIGBUFLEN];
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ /* First, test with an empty rdataset */
+ dns_rdatalist_init(&rdatalist);
+ rdatalist.rdclass = dns_rdataclass_in;
+ rdatalist.type = dns_rdatatype_none;
+ rdatalist.covers = dns_rdatatype_none;
+
+ dns_rdataset_init(&rdataset);
+ result = dns_rdatalist_tordataset(&rdatalist, &rdataset);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ isc_buffer_init(&target, buf, BIGBUFLEN);
+ result = dns_master_rdatasettotext(dns_rootname,
+ &rdataset, &dns_master_style_debug,
+ &target);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ ATF_CHECK_EQ(isc_buffer_usedlength(&target), 0);
+
+ /*
+ * XXX: We will also need to add tests for dumping various
+ * rdata types, classes, etc, and comparing the results against
+ * known-good output.
+ */
+
+ dns_test_end();
+}
+
+/* Raw load */
+ATF_TC(loadraw);
+ATF_TC_HEAD(loadraw, tc) {
+ atf_tc_set_md_var(tc, "descr", "dns_master_loadfile() loads a "
+ "valid raw file and returns success");
+}
+ATF_TC_BODY(loadraw, tc) {
+ isc_result_t result;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ /* Raw format version 0 */
+ result = test_master("testdata/master/master12.data",
+ dns_masterformat_raw, NULL, NULL);
+ ATF_CHECK_STREQ(isc_result_totext(result), "success");
+ ATF_CHECK(headerset);
+ ATF_CHECK_EQ(header.flags, 0);
+
+ /* Raw format version 1, no source serial */
+ result = test_master("testdata/master/master13.data",
+ dns_masterformat_raw, NULL, NULL);
+ ATF_CHECK_STREQ(isc_result_totext(result), "success");
+ ATF_CHECK(headerset);
+ ATF_CHECK_EQ(header.flags, 0);
+
+ /* Raw format version 1, source serial == 2011120101 */
+ result = test_master("testdata/master/master14.data",
+ dns_masterformat_raw, NULL, NULL);
+ ATF_CHECK_STREQ(isc_result_totext(result), "success");
+ ATF_CHECK(headerset);
+ ATF_CHECK((header.flags & DNS_MASTERRAW_SOURCESERIALSET) != 0);
+ ATF_CHECK_EQ(header.sourceserial, 2011120101);
+
+ dns_test_end();
+}
+
+/* Raw dump*/
+ATF_TC(dumpraw);
+ATF_TC_HEAD(dumpraw, tc) {
+ atf_tc_set_md_var(tc, "descr", "dns_master_dump*() functions "
+ "dump valid raw files");
+}
+ATF_TC_BODY(dumpraw, tc) {
+ isc_result_t result;
+ dns_db_t *db = NULL;
+ dns_dbversion_t *version = NULL;
+ char myorigin[sizeof(TEST_ORIGIN)];
+ dns_name_t dnsorigin;
+ isc_buffer_t source, target;
+ unsigned char namebuf[BUFLEN];
+ int len;
+
+ UNUSED(tc);
+
+ strlcpy(myorigin, TEST_ORIGIN, sizeof(myorigin));
+ len = strlen(myorigin);
+ isc_buffer_init(&source, myorigin, len);
+ isc_buffer_add(&source, len);
+ isc_buffer_setactive(&source, len);
+ isc_buffer_init(&target, namebuf, BUFLEN);
+ dns_name_init(&dnsorigin, NULL);
+ result = dns_name_fromtext(&dnsorigin, &source, dns_rootname,
+ 0, &target);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_db_create(mctx, "rbt", &dnsorigin, dns_dbtype_zone,
+ dns_rdataclass_in, 0, NULL, &db);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_db_load(db, "testdata/master/master1.data");
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ dns_db_currentversion(db, &version);
+
+ result = dns_master_dump2(mctx, db, version,
+ &dns_master_style_default, "test.dump",
+ dns_masterformat_raw);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = test_master("test.dump", dns_masterformat_raw, NULL, NULL);
+ ATF_CHECK_STREQ(isc_result_totext(result), "success");
+ ATF_CHECK(headerset);
+ ATF_CHECK_EQ(header.flags, 0);
+
+ dns_master_initrawheader(&header);
+ header.sourceserial = 12345;
+ header.flags |= DNS_MASTERRAW_SOURCESERIALSET;
+
+ unlink("test.dump");
+ result = dns_master_dump3(mctx, db, version,
+ &dns_master_style_default, "test.dump",
+ dns_masterformat_raw, &header);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = test_master("test.dump", dns_masterformat_raw, NULL, NULL);
+ ATF_CHECK_STREQ(isc_result_totext(result), "success");
+ ATF_CHECK(headerset);
+ ATF_CHECK((header.flags & DNS_MASTERRAW_SOURCESERIALSET) != 0);
+ ATF_CHECK_EQ(header.sourceserial, 12345);
+
+ unlink("test.dump");
+ dns_db_closeversion(db, &version, false);
+ dns_db_detach(&db);
+ dns_test_end();
+}
+
+static const char *warn_expect_value;
+static bool warn_expect_result;
+
+static void
+warn_expect(struct dns_rdatacallbacks *mycallbacks, const char *fmt, ...) {
+ char buf[4096];
+ va_list ap;
+
+ UNUSED(mycallbacks);
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ if (warn_expect_value != NULL && strstr(buf, warn_expect_value) != NULL)
+ warn_expect_result = true;
+}
+
+/* Origin change test */
+ATF_TC(neworigin);
+ATF_TC_HEAD(neworigin, tc) {
+ atf_tc_set_md_var(tc, "descr", "dns_master_loadfile() rejects "
+ "zones with inherited name following "
+ "$ORIGIN");
+}
+ATF_TC_BODY(neworigin, tc) {
+ isc_result_t result;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ warn_expect_value = "record with inherited owner";
+ warn_expect_result = false;
+ result = test_master("testdata/master/master17.data",
+ dns_masterformat_text, warn_expect, NULL);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ ATF_CHECK_MSG(warn_expect_result, "'%s' warning not emitted",
+ warn_expect_value);
+
+ dns_test_end();
+}
+
+/*
+ * Main
+ */
+ATF_TP_ADD_TCS(tp) {
+ ATF_TP_ADD_TC(tp, load);
+ ATF_TP_ADD_TC(tp, unexpected);
+ ATF_TP_ADD_TC(tp, noowner);
+ ATF_TP_ADD_TC(tp, nottl);
+ ATF_TP_ADD_TC(tp, badclass);
+ ATF_TP_ADD_TC(tp, dnskey);
+ ATF_TP_ADD_TC(tp, dnsnokey);
+ ATF_TP_ADD_TC(tp, include);
+ ATF_TP_ADD_TC(tp, master_includelist);
+ ATF_TP_ADD_TC(tp, includefail);
+ ATF_TP_ADD_TC(tp, blanklines);
+ ATF_TP_ADD_TC(tp, leadingzero);
+ ATF_TP_ADD_TC(tp, totext);
+ ATF_TP_ADD_TC(tp, loadraw);
+ ATF_TP_ADD_TC(tp, dumpraw);
+ ATF_TP_ADD_TC(tp, toobig);
+ ATF_TP_ADD_TC(tp, maxrdata);
+ ATF_TP_ADD_TC(tp, neworigin);
+
+ return (atf_no_error());
+}
diff --git a/lib/dns/tests/mkraw.pl b/lib/dns/tests/mkraw.pl
new file mode 100644
index 0000000..9791adc
--- /dev/null
+++ b/lib/dns/tests/mkraw.pl
@@ -0,0 +1,24 @@
+#!/usr/bin/perl -w
+#
+# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# See the COPYRIGHT file distributed with this work for additional
+# information regarding copyright ownership.
+
+# Convert a hexdump to binary format.
+#
+# To convert binary data to the input format for this command,
+# use the following:
+#
+# perl -e 'while (read(STDIN, my $byte, 1)) {
+# print unpack("H2", $byte);
+# }
+# print "\n";' < file > file.in
+
+use strict;
+chomp(my $line = <STDIN>);
+print pack("H*", $line);
diff --git a/lib/dns/tests/name_test.c b/lib/dns/tests/name_test.c
new file mode 100644
index 0000000..1953b41
--- /dev/null
+++ b/lib/dns/tests/name_test.c
@@ -0,0 +1,772 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#include <isc/buffer.h>
+#include <isc/commandline.h>
+#include <isc/mem.h>
+#include <isc/os.h>
+#include <isc/print.h>
+#include <isc/thread.h>
+#include <isc/util.h>
+
+#include <dns/compress.h>
+#include <dns/name.h>
+#include <dns/fixedname.h>
+
+#include "dnstest.h"
+
+/*
+ * Individual unit tests
+ */
+
+ATF_TC(fullcompare);
+ATF_TC_HEAD(fullcompare, tc) {
+ atf_tc_set_md_var(tc, "descr", "dns_name_fullcompare test");
+}
+ATF_TC_BODY(fullcompare, tc) {
+ dns_fixedname_t fixed1;
+ dns_fixedname_t fixed2;
+ dns_name_t *name1;
+ dns_name_t *name2;
+ dns_namereln_t relation;
+ int i;
+ isc_result_t result;
+ struct {
+ const char *name1;
+ const char *name2;
+ dns_namereln_t relation;
+ int order;
+ unsigned int nlabels;
+ } data[] = {
+ /* relative */
+ { "", "", dns_namereln_equal, 0, 0 },
+ { "foo", "", dns_namereln_subdomain, 1, 0 },
+ { "", "foo", dns_namereln_contains, -1, 0 },
+ { "foo", "bar", dns_namereln_none, 4, 0 },
+ { "bar", "foo", dns_namereln_none, -4, 0 },
+ { "bar.foo", "foo", dns_namereln_subdomain, 1, 1 },
+ { "foo", "bar.foo", dns_namereln_contains, -1, 1 },
+ { "baz.bar.foo", "bar.foo", dns_namereln_subdomain, 1, 2 },
+ { "bar.foo", "baz.bar.foo", dns_namereln_contains, -1, 2 },
+ { "foo.example", "bar.example", dns_namereln_commonancestor,
+ 4, 1 },
+
+ /* absolute */
+ { ".", ".", dns_namereln_equal, 0, 1 },
+ { "foo.", "bar.", dns_namereln_commonancestor, 4, 1 },
+ { "bar.", "foo.", dns_namereln_commonancestor, -4, 1 },
+ { "foo.example.", "bar.example.", dns_namereln_commonancestor,
+ 4, 2 },
+ { "bar.foo.", "foo.", dns_namereln_subdomain, 1, 2 },
+ { "foo.", "bar.foo.", dns_namereln_contains, -1, 2 },
+ { "baz.bar.foo.", "bar.foo.", dns_namereln_subdomain, 1, 3 },
+ { "bar.foo.", "baz.bar.foo.", dns_namereln_contains, -1, 3 },
+ { NULL, NULL, dns_namereln_none, 0, 0 }
+ };
+
+ UNUSED(tc);
+
+ name1 = dns_fixedname_initname(&fixed1);
+ name2 = dns_fixedname_initname(&fixed2);
+ for (i = 0; data[i].name1 != NULL; i++) {
+ int order = 3000;
+ unsigned int nlabels = 3000;
+
+ if (data[i].name1[0] == 0) {
+ dns_fixedname_init(&fixed1);
+ } else {
+ result = dns_name_fromstring2(name1, data[i].name1,
+ NULL, 0, NULL);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ }
+ if (data[i].name2[0] == 0) {
+ dns_fixedname_init(&fixed2);
+ } else {
+ result = dns_name_fromstring2(name2, data[i].name2,
+ NULL, 0, NULL);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ }
+ relation = dns_name_fullcompare(name1, name1, &order, &nlabels);
+ ATF_REQUIRE_EQ(relation, dns_namereln_equal);
+ ATF_REQUIRE_EQ(order, 0);
+ ATF_REQUIRE_EQ(nlabels, name1->labels);
+
+ /* Some random initializer */
+ order = 3001;
+ nlabels = 3001;
+
+ relation = dns_name_fullcompare(name1, name2, &order, &nlabels);
+ ATF_REQUIRE_EQ(relation, data[i].relation);
+ ATF_REQUIRE_EQ(order, data[i].order);
+ ATF_REQUIRE_EQ(nlabels, data[i].nlabels);
+ }
+}
+
+static void
+compress_test(dns_name_t *name1, dns_name_t *name2, dns_name_t *name3,
+ unsigned char *expected, unsigned int length,
+ dns_compress_t *cctx, dns_decompress_t *dctx)
+{
+ isc_buffer_t source;
+ isc_buffer_t target;
+ dns_name_t name;
+ unsigned char buf1[1024];
+ unsigned char buf2[1024];
+
+ isc_buffer_init(&source, buf1, sizeof(buf1));
+ isc_buffer_init(&target, buf2, sizeof(buf2));
+
+ ATF_REQUIRE_EQ(dns_name_towire(name1, cctx, &source), ISC_R_SUCCESS);
+
+ ATF_CHECK_EQ(dns_name_towire(name2, cctx, &source), ISC_R_SUCCESS);
+ ATF_CHECK_EQ(dns_name_towire(name2, cctx, &source), ISC_R_SUCCESS);
+ ATF_CHECK_EQ(dns_name_towire(name3, cctx, &source), ISC_R_SUCCESS);
+
+ isc_buffer_setactive(&source, source.used);
+
+ dns_name_init(&name, NULL);
+ RUNTIME_CHECK(dns_name_fromwire(&name, &source, dctx, false,
+ &target) == ISC_R_SUCCESS);
+ RUNTIME_CHECK(dns_name_fromwire(&name, &source, dctx, false,
+ &target) == ISC_R_SUCCESS);
+ RUNTIME_CHECK(dns_name_fromwire(&name, &source, dctx, false,
+ &target) == ISC_R_SUCCESS);
+ RUNTIME_CHECK(dns_name_fromwire(&name, &source, dctx, false,
+ &target) == ISC_R_SUCCESS);
+ dns_decompress_invalidate(dctx);
+
+ ATF_CHECK_EQ(target.used, length);
+ ATF_CHECK(memcmp(target.base, expected, target.used) == 0);
+}
+
+ATF_TC(compression);
+ATF_TC_HEAD(compression, tc) {
+ atf_tc_set_md_var(tc, "descr", "name compression test");
+}
+ATF_TC_BODY(compression, tc) {
+ unsigned int allowed;
+ dns_compress_t cctx;
+ dns_decompress_t dctx;
+ dns_name_t name1;
+ dns_name_t name2;
+ dns_name_t name3;
+ isc_region_t r;
+ unsigned char plain1[] = "\003yyy\003foo";
+ unsigned char plain2[] = "\003bar\003yyy\003foo";
+ unsigned char plain3[] = "\003xxx\003bar\003foo";
+ unsigned char plain[] = "\003yyy\003foo\0\003bar\003yyy\003foo\0\003"
+ "bar\003yyy\003foo\0\003xxx\003bar\003foo";
+
+ ATF_REQUIRE_EQ(dns_test_begin(NULL, false), ISC_R_SUCCESS);;
+
+ dns_name_init(&name1, NULL);
+ r.base = plain1;
+ r.length = sizeof(plain1);
+ dns_name_fromregion(&name1, &r);
+
+ dns_name_init(&name2, NULL);
+ r.base = plain2;
+ r.length = sizeof(plain2);
+ dns_name_fromregion(&name2, &r);
+
+ dns_name_init(&name3, NULL);
+ r.base = plain3;
+ r.length = sizeof(plain3);
+ dns_name_fromregion(&name3, &r);
+
+ /* Test 1: NONE */
+ allowed = DNS_COMPRESS_NONE;
+ ATF_REQUIRE_EQ(dns_compress_init(&cctx, -1, mctx), ISC_R_SUCCESS);
+ dns_compress_setmethods(&cctx, allowed);
+ dns_decompress_init(&dctx, -1, DNS_DECOMPRESS_STRICT);
+ dns_decompress_setmethods(&dctx, allowed);
+
+ compress_test(&name1, &name2, &name3, plain, sizeof(plain),
+ &cctx, &dctx);
+
+ dns_compress_rollback(&cctx, 0);
+ dns_compress_invalidate(&cctx);
+
+ /* Test2: GLOBAL14 */
+ allowed = DNS_COMPRESS_GLOBAL14;
+ ATF_REQUIRE_EQ(dns_compress_init(&cctx, -1, mctx), ISC_R_SUCCESS);
+ dns_compress_setmethods(&cctx, allowed);
+ dns_decompress_init(&dctx, -1, DNS_DECOMPRESS_STRICT);
+ dns_decompress_setmethods(&dctx, allowed);
+
+ compress_test(&name1, &name2, &name3, plain, sizeof(plain),
+ &cctx, &dctx);
+
+ dns_compress_rollback(&cctx, 0);
+ dns_compress_invalidate(&cctx);
+
+ /* Test3: ALL */
+ allowed = DNS_COMPRESS_ALL;
+ ATF_REQUIRE_EQ(dns_compress_init(&cctx, -1, mctx), ISC_R_SUCCESS);
+ dns_compress_setmethods(&cctx, allowed);
+ dns_decompress_init(&dctx, -1, DNS_DECOMPRESS_STRICT);
+ dns_decompress_setmethods(&dctx, allowed);
+
+ compress_test(&name1, &name2, &name3, plain, sizeof(plain),
+ &cctx, &dctx);
+
+ dns_compress_rollback(&cctx, 0);
+ dns_compress_invalidate(&cctx);
+
+ /* Test4: NONE disabled */
+ allowed = DNS_COMPRESS_NONE;
+ ATF_REQUIRE_EQ(dns_compress_init(&cctx, -1, mctx), ISC_R_SUCCESS);
+ dns_compress_setmethods(&cctx, allowed);
+ dns_compress_disable(&cctx);
+ dns_decompress_init(&dctx, -1, DNS_DECOMPRESS_STRICT);
+ dns_decompress_setmethods(&dctx, allowed);
+
+ compress_test(&name1, &name2, &name3, plain, sizeof(plain),
+ &cctx, &dctx);
+
+ dns_compress_rollback(&cctx, 0);
+ dns_compress_invalidate(&cctx);
+
+ /* Test5: GLOBAL14 disabled */
+ allowed = DNS_COMPRESS_GLOBAL14;
+ ATF_REQUIRE_EQ(dns_compress_init(&cctx, -1, mctx), ISC_R_SUCCESS);
+ dns_compress_setmethods(&cctx, allowed);
+ dns_compress_disable(&cctx);
+ dns_decompress_init(&dctx, -1, DNS_DECOMPRESS_STRICT);
+ dns_decompress_setmethods(&dctx, allowed);
+
+ compress_test(&name1, &name2, &name3, plain, sizeof(plain),
+ &cctx, &dctx);
+
+ dns_compress_rollback(&cctx, 0);
+ dns_compress_invalidate(&cctx);
+
+ /* Test6: ALL disabled */
+ allowed = DNS_COMPRESS_ALL;
+ ATF_REQUIRE_EQ(dns_compress_init(&cctx, -1, mctx), ISC_R_SUCCESS);
+ dns_compress_setmethods(&cctx, allowed);
+ dns_compress_disable(&cctx);
+ dns_decompress_init(&dctx, -1, DNS_DECOMPRESS_STRICT);
+ dns_decompress_setmethods(&dctx, allowed);
+
+ compress_test(&name1, &name2, &name3, plain, sizeof(plain),
+ &cctx, &dctx);
+
+ dns_compress_rollback(&cctx, 0);
+ dns_compress_invalidate(&cctx);
+
+ dns_test_end();
+}
+
+ATF_TC(istat);
+ATF_TC_HEAD(istat, tc) {
+ atf_tc_set_md_var(tc, "descr", "is trust-anchor-telemetry test");
+}
+ATF_TC_BODY(istat, tc) {
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+ isc_result_t result;
+ size_t i;
+ struct {
+ const char *name;
+ bool istat;
+ } data[] = {
+ { ".", false },
+ { "_ta-", false },
+ { "_ta-1234", true },
+ { "_TA-1234", true },
+ { "+TA-1234", false },
+ { "_fa-1234", false },
+ { "_td-1234", false },
+ { "_ta_1234", false },
+ { "_ta-g234", false },
+ { "_ta-1h34", false },
+ { "_ta-12i4", false },
+ { "_ta-123j", false },
+ { "_ta-1234-abcf", true },
+ { "_ta-1234-abcf-ED89", true },
+ { "_ta-12345-abcf-ED89", false },
+ { "_ta-.example", false },
+ { "_ta-1234.example", true },
+ { "_ta-1234-abcf.example", true },
+ { "_ta-1234-abcf-ED89.example", true },
+ { "_ta-12345-abcf-ED89.example", false },
+ { "_ta-1234-abcfe-ED89.example", false },
+ { "_ta-1234-abcf-EcD89.example", false }
+ };
+
+ name = dns_fixedname_initname(&fixed);
+
+ for (i = 0; i < sizeof(data)/sizeof(data[0]); i++) {
+ result = dns_name_fromstring(name, data[i].name, 0, NULL);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ ATF_CHECK_EQ_MSG(dns_name_istat(name), data[i].istat,
+ "testing %s - expected %u", data[i].name, data[i].istat);
+ }
+}
+
+ATF_TC(init);
+ATF_TC_HEAD(init, tc) {
+ atf_tc_set_md_var(tc, "descr", "dns_nane_init");
+}
+ATF_TC_BODY(init, tc) {
+ dns_name_t name;
+ unsigned char offsets[1];
+
+ dns_name_init(&name, offsets);
+
+ ATF_CHECK_EQ(name.ndata, NULL);
+ ATF_CHECK_EQ(name.length, 0);
+ ATF_CHECK_EQ(name.labels, 0);
+ ATF_CHECK_EQ(name.attributes, 0);
+ ATF_CHECK_EQ(name.offsets, offsets);
+ ATF_CHECK_EQ(name.buffer, NULL);
+}
+
+ATF_TC(invalidate);
+ATF_TC_HEAD(invalidate, tc) {
+ atf_tc_set_md_var(tc, "descr", "dns_nane_invalidate");
+}
+ATF_TC_BODY(invalidate, tc) {
+ dns_name_t name;
+ unsigned char offsets[1];
+
+ dns_name_init(&name, offsets);
+ dns_name_invalidate(&name);
+
+ ATF_CHECK_EQ(name.ndata, NULL);
+ ATF_CHECK_EQ(name.length, 0);
+ ATF_CHECK_EQ(name.labels, 0);
+ ATF_CHECK_EQ(name.attributes, 0);
+ ATF_CHECK_EQ(name.offsets, NULL);
+ ATF_CHECK_EQ(name.buffer, NULL);
+}
+
+ATF_TC(buffer);
+ATF_TC_HEAD(buffer, tc) {
+ atf_tc_set_md_var(tc, "descr", "dns_nane_setbuffer/hasbuffer");
+}
+ATF_TC_BODY(buffer, tc) {
+ dns_name_t name;
+ unsigned char buf[BUFSIZ];
+ isc_buffer_t b;
+
+ isc_buffer_init(&b, buf, BUFSIZ);
+ dns_name_init(&name, NULL);
+ dns_name_setbuffer(&name, &b);
+ ATF_CHECK_EQ(name.buffer, &b);
+ ATF_CHECK(dns_name_hasbuffer(&name));
+}
+
+ATF_TC(isabsolute);
+ATF_TC_HEAD(isabsolute, tc) {
+ atf_tc_set_md_var(tc, "descr", "dns_nane_isabsolute");
+}
+ATF_TC_BODY(isabsolute, tc) {
+ struct {
+ const char *namestr;
+ bool expect;
+ } testcases[] = {
+ { "x", false },
+ { "a.b.c.d.", true },
+ { "x.z", false}
+ };
+ unsigned int i;
+
+ for (i = 0; i < (sizeof(testcases)/sizeof(testcases[0])); i++) {
+ isc_result_t result;
+ dns_name_t name;
+ unsigned char data[BUFSIZ];
+ isc_buffer_t b, nb;
+ size_t len;
+
+ len = strlen(testcases[i].namestr);
+ isc_buffer_constinit(&b, testcases[i].namestr, len);
+ isc_buffer_add(&b, len);
+
+ dns_name_init(&name, NULL);
+ isc_buffer_init(&nb, data, BUFSIZ);
+ dns_name_setbuffer(&name, &nb);
+ result = dns_name_fromtext(&name, &b, NULL, 0, NULL);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ ATF_CHECK_EQ(dns_name_isabsolute(&name), testcases[i].expect);
+ }
+}
+
+ATF_TC(hash);
+ATF_TC_HEAD(hash, tc) {
+ atf_tc_set_md_var(tc, "descr", "dns_nane_hash");
+}
+ATF_TC_BODY(hash, tc) {
+ struct {
+ const char *name1;
+ const char *name2;
+ bool expect;
+ bool expecti;
+ } testcases[] = {
+ { "a.b.c.d", "A.B.C.D", true, false },
+ { "a.b.c.d.", "A.B.C.D.", true, false },
+ { "a.b.c.d", "a.b.c.d", true, true },
+ { "A.B.C.D.", "A.B.C.D.", true, false },
+ { "x.y.z.w", "a.b.c.d", false, false },
+ { "x.y.z.w.", "a.b.c.d.", false, false },
+ };
+ unsigned int i;
+
+ for (i = 0; i < (sizeof(testcases)/sizeof(testcases[0])); i++) {
+ isc_result_t result;
+ dns_fixedname_t f1, f2;
+ dns_name_t *n1, *n2;
+ unsigned int h1, h2;
+
+ n1 = dns_fixedname_initname(&f1);
+ n2 = dns_fixedname_initname(&f2);
+
+ result = dns_name_fromstring2(n1, testcases[i].name1,
+ NULL, 0, NULL);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ result = dns_name_fromstring2(n2, testcases[i].name2,
+ NULL, 0, NULL);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ /* Check case-insensitive hashing first */
+ h1 = dns_name_hash(n1, false);
+ h2 = dns_name_hash(n2, false);
+
+ printf("%s hashes to %u, %s to %u, case insensitive\n",
+ testcases[i].name1, h1, testcases[i].name2, h2);
+
+ ATF_REQUIRE_EQ((h1 == h2), testcases[i].expect);
+
+ /* Now case-sensitive */
+ h1 = dns_name_hash(n1, false);
+ h2 = dns_name_hash(n2, false);
+
+ printf("%s hashes to %u, %s to %u, case sensitive\n",
+ testcases[i].name1, h1, testcases[i].name2, h2);
+
+ ATF_REQUIRE_EQ((h1 == h2), testcases[i].expect);
+ }
+}
+
+ATF_TC(issubdomain);
+ATF_TC_HEAD(issubdomain, tc) {
+ atf_tc_set_md_var(tc, "descr", "dns_nane_issubdomain");
+}
+ATF_TC_BODY(issubdomain, tc) {
+ struct {
+ const char *name1;
+ const char *name2;
+ bool expect;
+ } testcases[] = {
+ { "c.d", "a.b.c.d", false },
+ { "c.d.", "a.b.c.d.", false },
+ { "b.c.d", "c.d", true },
+ { "a.b.c.d.", "c.d.", true },
+ { "a.b.c", "a.b.c", true },
+ { "a.b.c.", "a.b.c.", true },
+ { "x.y.z", "a.b.c", false}
+ };
+ unsigned int i;
+
+ for (i = 0; i < (sizeof(testcases)/sizeof(testcases[0])); i++) {
+ isc_result_t result;
+ dns_fixedname_t f1, f2;
+ dns_name_t *n1, *n2;
+
+ n1 = dns_fixedname_initname(&f1);
+ n2 = dns_fixedname_initname(&f2);
+
+ result = dns_name_fromstring2(n1, testcases[i].name1,
+ NULL, 0, NULL);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ result = dns_name_fromstring2(n2, testcases[i].name2,
+ NULL, 0, NULL);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ printf("check: %s %s a subdomain of %s\n",
+ testcases[i].name1,
+ testcases[i].expect ? "is" : "is not",
+ testcases[i].name2);
+
+ ATF_CHECK_EQ(dns_name_issubdomain(n1, n2),
+ testcases[i].expect);
+ }
+}
+
+ATF_TC(countlabels);
+ATF_TC_HEAD(countlabels, tc) {
+ atf_tc_set_md_var(tc, "descr", "dns_nane_countlabels");
+}
+ATF_TC_BODY(countlabels, tc) {
+ struct {
+ const char *namestr;
+ unsigned int expect;
+ } testcases[] = {
+ { "c.d", 2 },
+ { "c.d.", 3 },
+ { "a.b.c.d.", 5 },
+ { "a.b.c.d", 4 },
+ { "a.b.c", 3 },
+ { ".", 1 },
+ };
+ unsigned int i;
+
+ for (i = 0; i < (sizeof(testcases)/sizeof(testcases[0])); i++) {
+ isc_result_t result;
+ dns_fixedname_t fname;
+ dns_name_t *name;
+
+ name = dns_fixedname_initname(&fname);
+
+ result = dns_name_fromstring2(name, testcases[i].namestr,
+ NULL, 0, NULL);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ printf("%s: expect %u labels\n",
+ testcases[i].namestr, testcases[i].expect);
+
+ ATF_REQUIRE_EQ(dns_name_countlabels(name),
+ testcases[i].expect);
+ }
+}
+
+ATF_TC(getlabel);
+ATF_TC_HEAD(getlabel, tc) {
+ atf_tc_set_md_var(tc, "descr", "dns_nane_getlabel");
+}
+ATF_TC_BODY(getlabel, tc) {
+ struct {
+ const char *name1;
+ unsigned int pos1;
+ const char *name2;
+ unsigned int pos2;
+ } testcases[] = {
+ { "c.d", 1, "a.b.c.d", 3 },
+ { "a.b.c.d", 3, "c.d", 1 },
+ { "a.b.c.", 3, "A.B.C.", 3 },
+ };
+ unsigned int i;
+
+ for (i = 0; i < (sizeof(testcases)/sizeof(testcases[0])); i++) {
+ isc_result_t result;
+ dns_fixedname_t f1, f2;
+ dns_name_t *n1, *n2;
+ dns_label_t l1, l2;
+ unsigned char *p1, *p2;
+ unsigned int j;
+
+ n1 = dns_fixedname_initname(&f1);
+ n2 = dns_fixedname_initname(&f2);
+
+ result = dns_name_fromstring2(n1, testcases[i].name1,
+ NULL, 0, NULL);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ result = dns_name_fromstring2(n2, testcases[i].name2,
+ NULL, 0, NULL);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ dns_name_getlabel(n1, testcases[i].pos1, &l1);
+ dns_name_getlabel(n2, testcases[i].pos2, &l2);
+ ATF_CHECK_EQ(l1.length, l2.length);
+
+ p1 = l1.base;
+ p2 = l2.base;
+ for (j = 0; j < l1.length; j++) {
+ ATF_REQUIRE_EQ(*p1++, *p2++);
+ }
+ }
+}
+
+ATF_TC(getlabelsequence);
+ATF_TC_HEAD(getlabelsequence, tc) {
+ atf_tc_set_md_var(tc, "descr", "dns_nane_getlabelsequence");
+}
+ATF_TC_BODY(getlabelsequence, tc) {
+ struct {
+ const char *name1;
+ unsigned int pos1;
+ const char *name2;
+ unsigned int pos2;
+ unsigned int range;
+ } testcases[] = {
+ { "c.d", 1, "a.b.c.d", 3, 1 },
+ { "a.b.c.d.e", 2, "c.d", 0, 2 },
+ { "a.b.c", 0, "a.b.c", 0, 3 },
+
+ };
+ unsigned int i;
+
+ for (i = 0; i < (sizeof(testcases)/sizeof(testcases[0])); i++) {
+ isc_result_t result;
+ dns_name_t t1, t2;
+ dns_fixedname_t f1, f2;
+ dns_name_t *n1, *n2;
+
+ /* target names */
+ dns_name_init(&t1, NULL);
+ dns_name_init(&t2, NULL);
+
+ /* source names */
+ n1 = dns_fixedname_initname(&f1);
+ n2 = dns_fixedname_initname(&f2);
+
+ result = dns_name_fromstring2(n1, testcases[i].name1,
+ NULL, 0, NULL);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ result = dns_name_fromstring2(n2, testcases[i].name2,
+ NULL, 0, NULL);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ dns_name_getlabelsequence(n1, testcases[i].pos1,
+ testcases[i].range, &t1);
+ dns_name_getlabelsequence(n2, testcases[i].pos2,
+ testcases[i].range, &t2);
+
+ ATF_REQUIRE(dns_name_equal(&t1, &t2));
+ }
+}
+
+#ifdef ISC_PLATFORM_USETHREADS
+#ifdef DNS_BENCHMARK_TESTS
+
+/*
+ * XXXMUKS: Don't delete this code. It is useful in benchmarking the
+ * name parser, but we don't require it as part of the unit test runs.
+ */
+
+ATF_TC(benchmark);
+ATF_TC_HEAD(benchmark, tc) {
+ atf_tc_set_md_var(tc, "descr",
+ "Benchmark dns_name_fromwire() implementation");
+}
+
+static void *
+fromwire_thread(void *arg) {
+ unsigned int maxval = 32000000;
+ uint8_t data[] = {
+ 3, 'w', 'w', 'w',
+ 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e',
+ 7, 'i', 'n', 'v', 'a', 'l', 'i', 'd',
+ 0
+ };
+ unsigned char output_data[DNS_NAME_MAXWIRE];
+ isc_buffer_t source, target;
+ unsigned int i;
+ dns_decompress_t dctx;
+
+ UNUSED(arg);
+
+ dns_decompress_init(&dctx, -1, DNS_DECOMPRESS_STRICT);
+ dns_decompress_setmethods(&dctx, DNS_COMPRESS_NONE);
+
+ isc_buffer_init(&source, data, sizeof(data));
+ isc_buffer_add(&source, sizeof(data));
+ isc_buffer_init(&target, output_data, sizeof(output_data));
+
+ /* Parse 32 million names in each thread */
+ for (i = 0; i < maxval; i++) {
+ dns_name_t name;
+
+ isc_buffer_clear(&source);
+ isc_buffer_clear(&target);
+ isc_buffer_add(&source, sizeof(data));
+ isc_buffer_setactive(&source, sizeof(data));
+
+ dns_name_init(&name, NULL);
+ (void) dns_name_fromwire(&name, &source, &dctx, 0, &target);
+ }
+
+ return (NULL);
+}
+
+ATF_TC_BODY(benchmark, tc) {
+ isc_result_t result;
+ unsigned int i;
+ isc_time_t ts1, ts2;
+ double t;
+ unsigned int nthreads;
+ isc_thread_t threads[32];
+
+ UNUSED(tc);
+
+ debug_mem_record = false;
+
+ result = dns_test_begin(NULL, true);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = isc_time_now(&ts1);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ nthreads = ISC_MIN(isc_os_ncpus(), 32);
+ nthreads = ISC_MAX(nthreads, 1);
+ for (i = 0; i < nthreads; i++) {
+ result = isc_thread_create(fromwire_thread, NULL, &threads[i]);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ }
+
+ for (i = 0; i < nthreads; i++) {
+ result = isc_thread_join(threads[i], NULL);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ }
+
+ result = isc_time_now(&ts2);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ t = isc_time_microdiff(&ts2, &ts1);
+
+ printf("%u dns_name_fromwire() calls, %f seconds, %f calls/second\n",
+ nthreads * 32000000, t / 1000000.0,
+ (nthreads * 32000000) / (t / 1000000.0));
+
+ dns_test_end();
+}
+
+#endif /* DNS_BENCHMARK_TESTS */
+#endif /* ISC_PLATFORM_USETHREADS */
+
+/*
+ * Main
+ */
+ATF_TP_ADD_TCS(tp) {
+ ATF_TP_ADD_TC(tp, fullcompare);
+ ATF_TP_ADD_TC(tp, compression);
+ ATF_TP_ADD_TC(tp, istat);
+ ATF_TP_ADD_TC(tp, init);
+ ATF_TP_ADD_TC(tp, invalidate);
+ ATF_TP_ADD_TC(tp, buffer);
+ ATF_TP_ADD_TC(tp, isabsolute);
+ ATF_TP_ADD_TC(tp, hash);
+ ATF_TP_ADD_TC(tp, issubdomain);
+ ATF_TP_ADD_TC(tp, countlabels);
+ ATF_TP_ADD_TC(tp, getlabel);
+ ATF_TP_ADD_TC(tp, getlabelsequence);
+#ifdef ISC_PLATFORM_USETHREADS
+#ifdef DNS_BENCHMARK_TESTS
+ ATF_TP_ADD_TC(tp, benchmark);
+#endif /* DNS_BENCHMARK_TESTS */
+#endif /* ISC_PLATFORM_USETHREADS */
+
+ return (atf_no_error());
+}
diff --git a/lib/dns/tests/nsec3_test.c b/lib/dns/tests/nsec3_test.c
new file mode 100644
index 0000000..8a53344
--- /dev/null
+++ b/lib/dns/tests/nsec3_test.c
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <atf-c.h>
+
+#include <unistd.h>
+
+#include <dns/db.h>
+#include <dns/nsec3.h>
+
+#include "dnstest.h"
+
+#if defined(OPENSSL) || defined(PKCS11CRYPTO)
+/*
+ * Helper functions
+ */
+
+static void
+iteration_test(const char *file, unsigned int expected) {
+ isc_result_t result;
+ dns_db_t *db = NULL;
+ unsigned int iterations;
+
+ result = dns_test_loaddb(&db, dns_dbtype_zone, "test", file);
+ ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "%s", file);
+
+ result = dns_nsec3_maxiterations(db, NULL, mctx, &iterations);
+ ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "%s", file);
+
+ ATF_CHECK_EQ_MSG(iterations, expected, "%s", file);
+
+ dns_db_detach(&db);
+}
+
+/*%
+ * Structure containing parameters for nsec3param_salttotext_test().
+ */
+typedef struct {
+ const char *nsec3param_text; /* NSEC3PARAM RDATA in text form */
+ const char *expected_salt; /* string expected in target buffer */
+} nsec3param_salttotext_test_params_t;
+
+/*%
+ * Check whether dns_nsec3param_salttotext() handles supplied text form
+ * NSEC3PARAM RDATA correctly: test whether the result of calling the former is
+ * as expected and whether it properly checks available buffer space.
+ *
+ * Assumes supplied text form NSEC3PARAM RDATA is valid as testing handling of
+ * invalid NSEC3PARAM RDATA is out of scope of this unit test.
+ */
+static void
+nsec3param_salttotext_test(const nsec3param_salttotext_test_params_t *params) {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_nsec3param_t nsec3param;
+ unsigned char buf[1024];
+ isc_result_t result;
+ char salt[64];
+ size_t length;
+
+ /*
+ * Prepare a dns_rdata_nsec3param_t structure for testing.
+ */
+ result = dns_test_rdatafromstring(&rdata, dns_rdataclass_in,
+ dns_rdatatype_nsec3param, buf,
+ sizeof(buf),
+ params->nsec3param_text);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ result = dns_rdata_tostruct(&rdata, &nsec3param, NULL);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ /*
+ * Check typical use.
+ */
+ result = dns_nsec3param_salttotext(&nsec3param, salt, sizeof(salt));
+ ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS,
+ "\"%s\": expected success, got %s\n",
+ params->nsec3param_text, isc_result_totext(result));
+ ATF_CHECK_EQ_MSG(strcmp(salt, params->expected_salt), 0,
+ "\"%s\": expected salt \"%s\", got \"%s\"",
+ params->nsec3param_text, params->expected_salt, salt);
+
+ /*
+ * Ensure available space in the buffer is checked before the salt is
+ * printed to it and that the amount of space checked for includes the
+ * terminating NULL byte.
+ */
+ length = strlen(params->expected_salt);
+ ATF_REQUIRE(length < sizeof(salt) - 1); /* prevent buffer overwrite */
+ ATF_REQUIRE(length > 0U); /* prevent length underflow */
+
+ result = dns_nsec3param_salttotext(&nsec3param, salt, length - 1);
+ ATF_CHECK_EQ_MSG(result, ISC_R_NOSPACE,
+ "\"%s\": expected a %lu-byte target buffer to be "
+ "rejected, got %s\n",
+ params->nsec3param_text, (unsigned long)(length - 1),
+ isc_result_totext(result));
+ result = dns_nsec3param_salttotext(&nsec3param, salt, length);
+ ATF_CHECK_EQ_MSG(result, ISC_R_NOSPACE,
+ "\"%s\": expected a %lu-byte target buffer to be "
+ "rejected, got %s\n",
+ params->nsec3param_text, (unsigned long)length,
+ isc_result_totext(result));
+ result = dns_nsec3param_salttotext(&nsec3param, salt, length + 1);
+ ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS,
+ "\"%s\": expected a %lu-byte target buffer to be "
+ "accepted, got %s\n",
+ params->nsec3param_text, (unsigned long)(length + 1),
+ isc_result_totext(result));
+}
+
+/*
+ * Individual unit tests
+ */
+
+ATF_TC(max_iterations);
+ATF_TC_HEAD(max_iterations, tc) {
+ atf_tc_set_md_var(tc, "descr", "check that appropriate max iterations "
+ " is returned for different key size mixes");
+}
+ATF_TC_BODY(max_iterations, tc) {
+ isc_result_t result;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ iteration_test("testdata/nsec3/1024.db", 150);
+ iteration_test("testdata/nsec3/2048.db", 500);
+ iteration_test("testdata/nsec3/4096.db", 2500);
+ iteration_test("testdata/nsec3/min-1024.db", 150);
+ iteration_test("testdata/nsec3/min-2048.db", 500);
+
+ dns_test_end();
+}
+
+ATF_TC(nsec3param_salttotext);
+ATF_TC_HEAD(nsec3param_salttotext, tc) {
+ atf_tc_set_md_var(tc, "descr", "check dns_nsec3param_salttotext()");
+}
+ATF_TC_BODY(nsec3param_salttotext, tc) {
+ isc_result_t result;
+ size_t i;
+
+ const nsec3param_salttotext_test_params_t tests[] = {
+ /*
+ * Tests with non-empty salts.
+ */
+ { "0 0 10 0123456789abcdef", "0123456789ABCDEF" },
+ { "0 1 11 0123456789abcdef", "0123456789ABCDEF" },
+ { "1 0 12 42", "42" },
+ { "1 1 13 42", "42" },
+ /*
+ * Test with empty salt.
+ */
+ { "0 0 0 -", "-" },
+ };
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
+ nsec3param_salttotext_test(&tests[i]);
+ }
+
+ dns_test_end();
+}
+#else
+ATF_TC(untested);
+ATF_TC_HEAD(untested, tc) {
+ atf_tc_set_md_var(tc, "descr", "skipping nsec3 test");
+}
+ATF_TC_BODY(untested, tc) {
+ UNUSED(tc);
+ atf_tc_skip("DNSSEC not available");
+}
+#endif
+
+/*
+ * Main
+ */
+ATF_TP_ADD_TCS(tp) {
+#if defined(OPENSSL) || defined(PKCS11CRYPTO)
+ ATF_TP_ADD_TC(tp, max_iterations);
+ ATF_TP_ADD_TC(tp, nsec3param_salttotext);
+#else
+ ATF_TP_ADD_TC(tp, untested);
+#endif
+
+ return (atf_no_error());
+}
+
diff --git a/lib/dns/tests/peer_test.c b/lib/dns/tests/peer_test.c
new file mode 100644
index 0000000..1cff105
--- /dev/null
+++ b/lib/dns/tests/peer_test.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <atf-c.h>
+
+#include <unistd.h>
+
+#include <dns/peer.h>
+
+#include "dnstest.h"
+
+/*
+ * Individual unit tests
+ */
+ATF_TC(dscp);
+ATF_TC_HEAD(dscp, tc) {
+ atf_tc_set_md_var(tc, "descr",
+ "Test DSCP set/get functions");
+}
+ATF_TC_BODY(dscp, tc) {
+ isc_result_t result;
+ isc_netaddr_t netaddr;
+ struct in_addr ina;
+ dns_peer_t *peer = NULL;
+ isc_dscp_t dscp;
+
+ result = dns_test_begin(NULL, true);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ /*
+ * Create peer structure for the loopback address.
+ */
+ ina.s_addr = INADDR_LOOPBACK;
+ isc_netaddr_fromin(&netaddr, &ina);
+ result = dns_peer_new(mctx, &netaddr, &peer);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ /*
+ * All should be not set on creation.
+ * 'dscp' should remain unchanged.
+ */
+ dscp = 100;
+ result = dns_peer_getquerydscp(peer, &dscp);
+ ATF_REQUIRE_EQ(result, ISC_R_NOTFOUND);
+ ATF_REQUIRE_EQ(dscp, 100);
+
+ result = dns_peer_getnotifydscp(peer, &dscp);
+ ATF_REQUIRE_EQ(result, ISC_R_NOTFOUND);
+ ATF_REQUIRE_EQ(dscp, 100);
+
+ result = dns_peer_gettransferdscp(peer, &dscp);
+ ATF_REQUIRE_EQ(result, ISC_R_NOTFOUND);
+ ATF_REQUIRE_EQ(dscp, 100);
+
+ /*
+ * Test that setting query dscp does not affect the other
+ * dscp values. 'dscp' should remain unchanged until
+ * dns_peer_getquerydscp is called.
+ */
+ dscp = 100;
+ result = dns_peer_setquerydscp(peer, 1);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_peer_getnotifydscp(peer, &dscp);
+ ATF_REQUIRE_EQ(result, ISC_R_NOTFOUND);
+ ATF_REQUIRE_EQ(dscp, 100);
+
+ result = dns_peer_gettransferdscp(peer, &dscp);
+ ATF_REQUIRE_EQ(result, ISC_R_NOTFOUND);
+ ATF_REQUIRE_EQ(dscp, 100);
+
+ result = dns_peer_getquerydscp(peer, &dscp);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ ATF_REQUIRE_EQ(dscp, 1);
+
+ /*
+ * Test that setting notify dscp does not affect the other
+ * dscp values. 'dscp' should remain unchanged until
+ * dns_peer_getquerydscp is called then should change again
+ * on dns_peer_getnotifydscp.
+ */
+ dscp = 100;
+ result = dns_peer_setnotifydscp(peer, 2);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_peer_gettransferdscp(peer, &dscp);
+ ATF_REQUIRE_EQ(result, ISC_R_NOTFOUND);
+ ATF_REQUIRE_EQ(dscp, 100);
+
+ result = dns_peer_getquerydscp(peer, &dscp);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ ATF_REQUIRE_EQ(dscp, 1);
+
+ result = dns_peer_getnotifydscp(peer, &dscp);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ ATF_REQUIRE_EQ(dscp, 2);
+
+ /*
+ * Test that setting notify dscp does not affect the other
+ * dscp values. Check that appropriate values are returned.
+ */
+ dscp = 100;
+ result = dns_peer_settransferdscp(peer, 3);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_peer_getquerydscp(peer, &dscp);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ ATF_REQUIRE_EQ(dscp, 1);
+
+ result = dns_peer_getnotifydscp(peer, &dscp);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ ATF_REQUIRE_EQ(dscp, 2);
+
+ result = dns_peer_gettransferdscp(peer, &dscp);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ ATF_REQUIRE_EQ(dscp, 3);
+
+ dns_peer_detach(&peer);
+ dns_test_end();
+}
+
+/*
+ * Main
+ */
+ATF_TP_ADD_TCS(tp) {
+ ATF_TP_ADD_TC(tp, dscp);
+ return (atf_no_error());
+}
diff --git a/lib/dns/tests/private_test.c b/lib/dns/tests/private_test.c
new file mode 100644
index 0000000..dcb859e
--- /dev/null
+++ b/lib/dns/tests/private_test.c
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <atf-c.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <unistd.h>
+
+#include <isc/buffer.h>
+
+#include <dns/nsec3.h>
+#include <dns/private.h>
+#include <dns/rdataclass.h>
+#include <dns/rdatatype.h>
+
+#include <dst/dst.h>
+
+#include "dnstest.h"
+
+static dns_rdatatype_t privatetype = 65534;
+
+typedef struct {
+ unsigned char alg;
+ dns_keytag_t keyid;
+ bool remove;
+ bool complete;
+} signing_testcase_t;
+
+typedef struct {
+ unsigned char hash;
+ unsigned char flags;
+ unsigned int iterations;
+ unsigned long salt;
+ bool remove;
+ bool pending;
+ bool nonsec;
+} nsec3_testcase_t;
+
+/*
+ * Helper functions
+ */
+static void
+make_signing(signing_testcase_t *testcase, dns_rdata_t *private,
+ unsigned char *buf, size_t len)
+{
+ dns_rdata_init(private);
+
+ buf[0] = testcase->alg;
+ buf[1] = (testcase->keyid & 0xff00) >> 8;
+ buf[2] = (testcase->keyid & 0xff);
+ buf[3] = testcase->remove;
+ buf[4] = testcase->complete;
+ private->data = buf;
+ private->length = len;
+ private->type = privatetype;
+ private->rdclass = dns_rdataclass_in;
+}
+
+static void
+make_nsec3(nsec3_testcase_t *testcase, dns_rdata_t *private,
+ unsigned char *pbuf)
+{
+ dns_rdata_nsec3param_t params;
+ dns_rdata_t nsec3param = DNS_RDATA_INIT;
+ unsigned char bufdata[BUFSIZ];
+ isc_buffer_t buf;
+ uint32_t salt;
+ unsigned char *sp;
+ int slen = 4;
+
+ /* for simplicity, we're using a maximum salt length of 4 */
+ salt = htonl(testcase->salt);
+ sp = (unsigned char *) &salt;
+ while (*sp == '\0' && slen > 0) {
+ slen--;
+ sp++;
+ }
+
+ params.common.rdclass = dns_rdataclass_in;
+ params.common.rdtype = dns_rdatatype_nsec3param;
+ params.hash = testcase->hash;
+ params.iterations = testcase->iterations;
+ params.salt = sp;
+ params.salt_length = slen;
+
+ params.flags = testcase->flags;
+ if (testcase->remove) {
+ params.flags |= DNS_NSEC3FLAG_REMOVE;
+ if (testcase->nonsec)
+ params.flags |= DNS_NSEC3FLAG_NONSEC;
+ } else {
+ params.flags |= DNS_NSEC3FLAG_CREATE;
+ if (testcase->pending)
+ params.flags |= DNS_NSEC3FLAG_INITIAL;
+ }
+
+ isc_buffer_init(&buf, bufdata, sizeof(bufdata));
+ dns_rdata_fromstruct(&nsec3param, dns_rdataclass_in,
+ dns_rdatatype_nsec3param, &params, &buf);
+
+ dns_rdata_init(private);
+
+ dns_nsec3param_toprivate(&nsec3param, private, privatetype,
+ pbuf, DNS_NSEC3PARAM_BUFFERSIZE + 1);
+}
+
+/*
+ * Individual unit tests
+ */
+ATF_TC(private_signing_totext);
+ATF_TC_HEAD(private_signing_totext, tc) {
+ atf_tc_set_md_var(tc, "descr",
+ "convert private signing records to text");
+}
+ATF_TC_BODY(private_signing_totext, tc) {
+ isc_result_t result;
+ dns_rdata_t private;
+ int i;
+
+ signing_testcase_t testcases[] = {
+ { DST_ALG_RSASHA512, 12345, 0, 0 },
+ { DST_ALG_RSASHA256, 54321, 1, 0 },
+ { DST_ALG_NSEC3RSASHA1, 22222, 0, 1 },
+ { DST_ALG_RSASHA1, 33333, 1, 1 }
+ };
+ const char *results[] = {
+ "Signing with key 12345/RSASHA512",
+ "Removing signatures for key 54321/RSASHA256",
+ "Done signing with key 22222/NSEC3RSASHA1",
+ "Done removing signatures for key 33333/RSASHA1"
+ };
+ int ncases = 4;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, true);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ for (i = 0; i < ncases; i++) {
+ unsigned char data[5];
+ char output[BUFSIZ];
+ isc_buffer_t buf;
+
+ isc_buffer_init(&buf, output, sizeof(output));
+
+ make_signing(&testcases[i], &private, data, sizeof(data));
+ dns_private_totext(&private, &buf);
+ ATF_CHECK_STREQ(output, results[i]);
+ }
+
+ dns_test_end();
+}
+
+ATF_TC(private_nsec3_totext);
+ATF_TC_HEAD(private_nsec3_totext, tc) {
+ atf_tc_set_md_var(tc, "descr", "convert private chain records to text");
+}
+ATF_TC_BODY(private_nsec3_totext, tc) {
+ isc_result_t result;
+ dns_rdata_t private;
+ int i;
+
+ nsec3_testcase_t testcases[] = {
+ { 1, 0, 1, 0xbeef, 0, 0, 0 },
+ { 1, 1, 10, 0xdadd, 0, 0, 0 },
+ { 1, 0, 20, 0xbead, 0, 1, 0 },
+ { 1, 0, 30, 0xdeaf, 1, 0, 0 },
+ { 1, 0, 100, 0xfeedabee, 1, 0, 1 },
+ };
+ const char *results[] = {
+ "Creating NSEC3 chain 1 0 1 BEEF",
+ "Creating NSEC3 chain 1 1 10 DADD",
+ "Pending NSEC3 chain 1 0 20 BEAD",
+ "Removing NSEC3 chain 1 0 30 DEAF / creating NSEC chain",
+ "Removing NSEC3 chain 1 0 100 FEEDABEE"
+ };
+ int ncases = 5;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, true);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ for (i = 0; i < ncases; i++) {
+ unsigned char data[DNS_NSEC3PARAM_BUFFERSIZE + 1];
+ char output[BUFSIZ];
+ isc_buffer_t buf;
+
+ isc_buffer_init(&buf, output, sizeof(output));
+
+ make_nsec3(&testcases[i], &private, data);
+ dns_private_totext(&private, &buf);
+ ATF_CHECK_STREQ(output, results[i]);
+ }
+
+ dns_test_end();
+}
+
+/*
+ * Main
+ */
+ATF_TP_ADD_TCS(tp) {
+ ATF_TP_ADD_TC(tp, private_signing_totext);
+ ATF_TP_ADD_TC(tp, private_nsec3_totext);
+ return (atf_no_error());
+}
diff --git a/lib/dns/tests/rbt_serialize_test.c b/lib/dns/tests/rbt_serialize_test.c
new file mode 100644
index 0000000..07d4e56
--- /dev/null
+++ b/lib/dns/tests/rbt_serialize_test.c
@@ -0,0 +1,456 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/* ! \file */
+
+#include <config.h>
+
+#include <atf-c.h>
+
+#include <inttypes.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/random.h>
+#include <isc/string.h>
+
+#include <dns/rbt.h>
+#include <dns/fixedname.h>
+#include <dns/result.h>
+#include <dns/compress.h>
+#include "dnstest.h"
+
+#include <isc/app.h>
+#include <isc/buffer.h>
+#include <isc/entropy.h>
+#include <isc/file.h>
+#include <isc/hash.h>
+#include <isc/mem.h>
+#include <isc/os.h>
+#include <isc/string.h>
+#include <isc/socket.h>
+#include <isc/stdio.h>
+#include <isc/task.h>
+#include <isc/timer.h>
+#include <isc/util.h>
+
+#include <dns/log.h>
+#include <dns/name.h>
+#include <dns/result.h>
+
+#include <dst/dst.h>
+
+#ifndef MAP_FILE
+#define MAP_FILE 0
+#endif
+
+typedef struct data_holder {
+ int len;
+ const char *data;
+} data_holder_t;
+
+typedef struct rbt_testdata {
+ const char *name;
+ size_t name_len;
+ data_holder_t data;
+} rbt_testdata_t;
+
+#define DATA_ITEM(name) { (name), sizeof(name) - 1, { sizeof(name), (name) } }
+
+rbt_testdata_t testdata[] = {
+ DATA_ITEM("first.com."),
+ DATA_ITEM("one.net."),
+ DATA_ITEM("two.com."),
+ DATA_ITEM("three.org."),
+ DATA_ITEM("asdf.com."),
+ DATA_ITEM("ghjkl.com."),
+ DATA_ITEM("1.edu."),
+ DATA_ITEM("2.edu."),
+ DATA_ITEM("3.edu."),
+ DATA_ITEM("123.edu."),
+ DATA_ITEM("1236.com."),
+ DATA_ITEM("and_so_forth.com."),
+ DATA_ITEM("thisisalongname.com."),
+ DATA_ITEM("a.b."),
+ DATA_ITEM("test.net."),
+ DATA_ITEM("whoknows.org."),
+ DATA_ITEM("blargh.com."),
+ DATA_ITEM("www.joe.com."),
+ DATA_ITEM("test.com."),
+ DATA_ITEM("isc.org."),
+ DATA_ITEM("uiop.mil."),
+ DATA_ITEM("last.fm."),
+ { NULL, 0, { 0, NULL } }
+};
+
+static void
+delete_data(void *data, void *arg) {
+ UNUSED(arg);
+ UNUSED(data);
+}
+
+static isc_result_t
+write_data(FILE *file, unsigned char *datap, void *arg, uint64_t *crc) {
+ isc_result_t result;
+ size_t ret = 0;
+ data_holder_t *data = (data_holder_t *)datap;
+ data_holder_t temp;
+ off_t where;
+
+ UNUSED(arg);
+
+ REQUIRE(file != NULL);
+ REQUIRE(crc != NULL);
+ REQUIRE(data != NULL);
+ REQUIRE((data->len == 0 && data->data == NULL) ||
+ (data->len != 0 && data->data != NULL));
+
+ result = isc_stdio_tell(file, &where);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ temp = *data;
+ temp.data = (data->len == 0
+ ? NULL
+ : (char *)((uintptr_t)where + sizeof(data_holder_t)));
+
+ isc_crc64_update(crc, (void *)&temp, sizeof(temp));
+ ret = fwrite(&temp, sizeof(data_holder_t), 1, file);
+ if (ret != 1)
+ return (ISC_R_FAILURE);
+ if (data->len > 0) {
+ isc_crc64_update(crc, (const void *)data->data, data->len);
+ ret = fwrite(data->data, data->len, 1, file);
+ if (ret != 1)
+ return (ISC_R_FAILURE);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+fix_data(dns_rbtnode_t *p, void *base, size_t max, void *arg,
+ uint64_t *crc)
+{
+ data_holder_t *data = p->data;
+ size_t size;
+
+ UNUSED(base);
+ UNUSED(max);
+ UNUSED(arg);
+
+ REQUIRE(crc != NULL);
+ REQUIRE(p != NULL);
+
+
+ if (data == NULL)
+ printf("fixing data: data NULL\n");
+ else
+ printf("fixing data: len %d, data %p\n", data->len, data->data);
+
+ if (data == NULL ||
+ (data->len == 0 && data->data != NULL) ||
+ (data->len != 0 && data->data == NULL))
+ return (ISC_R_INVALIDFILE);
+
+ size = max - ((char *)p - (char *)base);
+
+ if (data->len > (int) size || data->data > (const char *) max) {
+ printf("data invalid\n");
+ return (ISC_R_INVALIDFILE);
+ }
+
+ isc_crc64_update(crc, (void *)data, sizeof(*data));
+
+ data->data = (data->len == 0)
+ ? NULL
+ : (char *)data + sizeof(data_holder_t);
+
+ if (data->len > 0)
+ isc_crc64_update(crc, (const void *)data->data, data->len);
+
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Load test data into the RBT.
+ */
+static void
+add_test_data(isc_mem_t *mymctx, dns_rbt_t *rbt) {
+ char buffer[1024];
+ isc_buffer_t b;
+ isc_result_t result;
+ dns_fixedname_t fname;
+ dns_name_t *name;
+ dns_compress_t cctx;
+ rbt_testdata_t *testdatap = testdata;
+
+ dns_compress_init(&cctx, -1, mymctx);
+
+ while (testdatap->name != NULL && testdatap->data.data != NULL) {
+ memmove(buffer, testdatap->name, testdatap->name_len);
+
+ isc_buffer_init(&b, buffer, testdatap->name_len);
+ isc_buffer_add(&b, testdatap->name_len);
+ name = dns_fixedname_initname(&fname);
+ result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL);
+ if (result != ISC_R_SUCCESS) {
+ testdatap++;
+ continue;
+ }
+
+ if (name != NULL) {
+ result = dns_rbt_addname(rbt, name, &testdatap->data);
+ ATF_CHECK_STREQ(dns_result_totext(result), "success");
+ }
+ testdatap++;
+ }
+
+ dns_compress_invalidate(&cctx);
+}
+
+/*
+ * Walk the tree and ensure that all the test nodes are present.
+ */
+static void
+check_test_data(dns_rbt_t *rbt) {
+ char buffer[1024];
+ char *arg;
+ dns_fixedname_t fname;
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+ isc_buffer_t b;
+ data_holder_t *data;
+ isc_result_t result;
+ dns_name_t *foundname;
+ rbt_testdata_t *testdatap = testdata;
+
+ foundname = dns_fixedname_initname(&fixed);
+
+ while (testdatap->name != NULL && testdatap->data.data != NULL) {
+ memmove(buffer, testdatap->name, testdatap->name_len + 1);
+ arg = buffer;
+
+ isc_buffer_init(&b, arg, testdatap->name_len);
+ isc_buffer_add(&b, testdatap->name_len);
+ name = dns_fixedname_initname(&fname);
+ result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL);
+ if (result != ISC_R_SUCCESS) {
+ testdatap++;
+ continue;
+ }
+
+ data = NULL;
+ result = dns_rbt_findname(rbt, name, 0, foundname,
+ (void *) &data);
+ ATF_CHECK_STREQ(dns_result_totext(result), "success");
+
+ testdatap++;
+ }
+}
+
+static void
+data_printer(FILE *out, void *datap)
+{
+ data_holder_t *data = (data_holder_t *)datap;
+
+ fprintf(out, "%d bytes, %s", data->len, data->data);
+}
+
+ATF_TC(serialize);
+ATF_TC_HEAD(serialize, tc) {
+ atf_tc_set_md_var(tc, "descr", "Test writing an rbt to file");
+}
+ATF_TC_BODY(serialize, tc) {
+ dns_rbt_t *rbt = NULL;
+ isc_result_t result;
+ FILE *rbtfile = NULL;
+ dns_rbt_t *rbt_deserialized = NULL;
+ off_t offset;
+ int fd;
+ off_t filesize = 0;
+ char *base;
+
+ UNUSED(tc);
+
+ isc_mem_debugging = ISC_MEM_DEBUGRECORD;
+
+ result = dns_test_begin(NULL, true);
+ ATF_CHECK_STREQ(dns_result_totext(result), "success");
+ result = dns_rbt_create(mctx, delete_data, NULL, &rbt);
+ ATF_CHECK_STREQ(dns_result_totext(result), "success");
+
+ add_test_data(mctx, rbt);
+
+ dns_rbt_printtext(rbt, data_printer, stdout);
+
+ /*
+ * Serialize the tree.
+ */
+ printf("serialization begins.\n");
+ rbtfile = fopen("./zone.bin", "w+b");
+ ATF_REQUIRE(rbtfile != NULL);
+ result = dns_rbt_serialize_tree(rbtfile, rbt, write_data, NULL,
+ &offset);
+ ATF_REQUIRE(result == ISC_R_SUCCESS);
+ dns_rbt_destroy(&rbt);
+
+ /*
+ * Deserialize the tree
+ */
+ printf("deserialization begins.\n");
+
+ /*
+ * Map in the whole file in one go
+ */
+ fd = open("zone.bin", O_RDWR);
+ isc_file_getsizefd(fd, &filesize);
+ base = mmap(NULL, filesize,
+ PROT_READ|PROT_WRITE,
+ MAP_FILE|MAP_PRIVATE, fd, 0);
+ ATF_REQUIRE(base != NULL && base != MAP_FAILED);
+ close(fd);
+
+ result = dns_rbt_deserialize_tree(base, filesize, 0, mctx,
+ delete_data, NULL, fix_data, NULL,
+ NULL, &rbt_deserialized);
+
+ /* Test to make sure we have a valid tree */
+ ATF_REQUIRE(result == ISC_R_SUCCESS);
+ if (rbt_deserialized == NULL)
+ atf_tc_fail("deserialized rbt is null!"); /* Abort execution. */
+
+ check_test_data(rbt_deserialized);
+
+ dns_rbt_printtext(rbt_deserialized, data_printer, stdout);
+
+ dns_rbt_destroy(&rbt_deserialized);
+ munmap(base, filesize);
+ unlink("zone.bin");
+ dns_test_end();
+}
+
+ATF_TC(deserialize_corrupt);
+ATF_TC_HEAD(deserialize_corrupt, tc) {
+ atf_tc_set_md_var(tc, "descr", "Test reading a corrupt map file");
+}
+ATF_TC_BODY(deserialize_corrupt, tc) {
+ dns_rbt_t *rbt = NULL;
+ isc_result_t result;
+ FILE *rbtfile = NULL;
+ off_t offset;
+ int fd;
+ off_t filesize = 0;
+ char *base, *p, *q;
+ uint32_t r;
+ int i;
+
+ UNUSED(tc);
+
+ isc_mem_debugging = ISC_MEM_DEBUGRECORD;
+
+ result = dns_test_begin(NULL, true);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ /* Set up map file */
+ result = dns_rbt_create(mctx, delete_data, NULL, &rbt);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ add_test_data(mctx, rbt);
+ rbtfile = fopen("./zone.bin", "w+b");
+ ATF_REQUIRE(rbtfile != NULL);
+ result = dns_rbt_serialize_tree(rbtfile, rbt, write_data, NULL,
+ &offset);
+ ATF_REQUIRE(result == ISC_R_SUCCESS);
+ dns_rbt_destroy(&rbt);
+
+ /* Read back with random fuzzing */
+ for (i = 0; i < 256; i++) {
+ dns_rbt_t *rbt_deserialized = NULL;
+
+ fd = open("zone.bin", O_RDWR);
+ isc_file_getsizefd(fd, &filesize);
+ base = mmap(NULL, filesize,
+ PROT_READ|PROT_WRITE,
+ MAP_FILE|MAP_PRIVATE, fd, 0);
+ ATF_REQUIRE(base != NULL && base != MAP_FAILED);
+ close(fd);
+
+ /* Randomly fuzz a portion of the memory */
+ isc_random_get(&r);
+ p = base + (r % filesize);
+ q = base + filesize;
+ isc_random_get(&r);
+ q -= (r % (q - p));
+ while (p++ < q) {
+ isc_random_get(&r);
+ *p = r & 0xff;
+ }
+
+ result = dns_rbt_deserialize_tree(base, filesize, 0, mctx,
+ delete_data, NULL,
+ fix_data, NULL,
+ NULL, &rbt_deserialized);
+ printf("%d: %s\n", i, isc_result_totext(result));
+
+ /* Test to make sure we have a valid tree */
+ ATF_REQUIRE(result == ISC_R_SUCCESS ||
+ result == ISC_R_INVALIDFILE);
+ if (result != ISC_R_SUCCESS)
+ ATF_REQUIRE(rbt_deserialized == NULL);
+
+ if (rbt_deserialized != NULL)
+ dns_rbt_destroy(&rbt_deserialized);
+
+ munmap(base, filesize);
+ }
+
+ unlink("zone.bin");
+ dns_test_end();
+}
+
+
+ATF_TC(serialize_align);
+ATF_TC_HEAD(serialize_align, tc) {
+ atf_tc_set_md_var(tc, "descr",
+ "Test the dns_rbt_serialize_align() function.");
+}
+ATF_TC_BODY(serialize_align, tc) {
+ UNUSED(tc);
+
+ ATF_CHECK(dns_rbt_serialize_align(0) == 0);
+ ATF_CHECK(dns_rbt_serialize_align(1) == 8);
+ ATF_CHECK(dns_rbt_serialize_align(2) == 8);
+ ATF_CHECK(dns_rbt_serialize_align(3) == 8);
+ ATF_CHECK(dns_rbt_serialize_align(4) == 8);
+ ATF_CHECK(dns_rbt_serialize_align(5) == 8);
+ ATF_CHECK(dns_rbt_serialize_align(6) == 8);
+ ATF_CHECK(dns_rbt_serialize_align(7) == 8);
+ ATF_CHECK(dns_rbt_serialize_align(8) == 8);
+ ATF_CHECK(dns_rbt_serialize_align(9) == 16);
+ ATF_CHECK(dns_rbt_serialize_align(0xff) == 0x100);
+ ATF_CHECK(dns_rbt_serialize_align(0x301) == 0x308);
+}
+
+/*
+ * Main
+ */
+ATF_TP_ADD_TCS(tp) {
+ ATF_TP_ADD_TC(tp, serialize);
+ ATF_TP_ADD_TC(tp, deserialize_corrupt);
+ ATF_TP_ADD_TC(tp, serialize_align);
+
+ return (atf_no_error());
+}
diff --git a/lib/dns/tests/rbt_test.c b/lib/dns/tests/rbt_test.c
new file mode 100644
index 0000000..81d97d4
--- /dev/null
+++ b/lib/dns/tests/rbt_test.c
@@ -0,0 +1,1437 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* ! \file */
+
+#include <config.h>
+#include <atf-c.h>
+#include <isc/mem.h>
+#include <isc/random.h>
+#include <isc/string.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <inttypes.h> /* uintptr_t */
+#include <stdbool.h>
+
+#include <dns/rbt.h>
+#include <dns/fixedname.h>
+#include <dns/name.h>
+#include <dns/result.h>
+#include <dns/compress.h>
+#include "dnstest.h"
+
+#include <isc/app.h>
+#include <isc/buffer.h>
+#include <isc/entropy.h>
+#include <isc/file.h>
+#include <isc/hash.h>
+#include <isc/mem.h>
+#include <isc/os.h>
+#include <isc/string.h>
+#include <isc/socket.h>
+#include <isc/stdio.h>
+#include <isc/task.h>
+#include <isc/thread.h>
+#include <isc/timer.h>
+#include <isc/util.h>
+#include <isc/print.h>
+#include <isc/time.h>
+
+#include <dns/log.h>
+#include <dns/name.h>
+#include <dns/result.h>
+
+#include <dst/dst.h>
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <time.h>
+
+typedef struct {
+ dns_rbt_t *rbt;
+ dns_rbt_t *rbt_distances;
+} test_context_t;
+
+/* The initial structure of domain tree will be as follows:
+ *
+ * .
+ * |
+ * b
+ * / \
+ * a d.e.f
+ * / | \
+ * c | g.h
+ * | |
+ * w.y i
+ * / | \ \
+ * x | z k
+ * | |
+ * p j
+ * / \
+ * o q
+ */
+
+/* The full absolute names of the nodes in the tree (the tree also
+ * contains "." which is not included in this list).
+ */
+static const char * const domain_names[] = {
+ "c", "b", "a", "x.d.e.f", "z.d.e.f", "g.h", "i.g.h", "o.w.y.d.e.f",
+ "j.z.d.e.f", "p.w.y.d.e.f", "q.w.y.d.e.f", "k.g.h"
+};
+
+static const size_t domain_names_count = (sizeof(domain_names) /
+ sizeof(domain_names[0]));
+
+/* These are set as the node data for the tree used in distances check
+ * (for the names in domain_names[] above).
+ */
+static const int node_distances[] = {
+ 3, 1, 2, 2, 2, 3, 1, 2, 1, 1, 2, 2
+};
+
+/*
+ * The domain order should be:
+ * ., a, b, c, d.e.f, x.d.e.f, w.y.d.e.f, o.w.y.d.e.f, p.w.y.d.e.f,
+ * q.w.y.d.e.f, z.d.e.f, j.z.d.e.f, g.h, i.g.h, k.g.h
+ * . (no data, can't be found)
+ * |
+ * b
+ * / \
+ * a d.e.f
+ * / | \
+ * c | g.h
+ * | |
+ * w.y i
+ * / | \ \
+ * x | z k
+ * | |
+ * p j
+ * / \
+ * o q
+ */
+
+static const char * const ordered_names[] = {
+ "a", "b", "c", "d.e.f", "x.d.e.f", "w.y.d.e.f", "o.w.y.d.e.f",
+ "p.w.y.d.e.f", "q.w.y.d.e.f", "z.d.e.f", "j.z.d.e.f",
+ "g.h", "i.g.h", "k.g.h"};
+
+static const size_t ordered_names_count = (sizeof(ordered_names) /
+ sizeof(*ordered_names));
+
+static void
+delete_data(void *data, void *arg) {
+ UNUSED(arg);
+
+ isc_mem_put(mctx, data, sizeof(size_t));
+}
+
+static test_context_t *
+test_context_setup(void) {
+ test_context_t *ctx;
+ isc_result_t result;
+ size_t i;
+
+ ctx = isc_mem_get(mctx, sizeof(*ctx));
+ ATF_REQUIRE(ctx != NULL);
+
+ ctx->rbt = NULL;
+ result = dns_rbt_create(mctx, delete_data, NULL, &ctx->rbt);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ ctx->rbt_distances = NULL;
+ result = dns_rbt_create(mctx, delete_data, NULL, &ctx->rbt_distances);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ for (i = 0; i < domain_names_count; i++) {
+ size_t *n;
+ dns_fixedname_t fname;
+ dns_name_t *name;
+
+ dns_test_namefromstring(domain_names[i], &fname);
+
+ name = dns_fixedname_name(&fname);
+
+ n = isc_mem_get(mctx, sizeof(size_t));
+ ATF_REQUIRE(n != NULL);
+ *n = i + 1;
+ result = dns_rbt_addname(ctx->rbt, name, n);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ n = isc_mem_get(mctx, sizeof(size_t));
+ ATF_REQUIRE(n != NULL);
+ *n = node_distances[i];
+ result = dns_rbt_addname(ctx->rbt_distances, name, n);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ }
+
+ return (ctx);
+}
+
+static void
+test_context_teardown(test_context_t *ctx) {
+ dns_rbt_destroy(&ctx->rbt);
+ dns_rbt_destroy(&ctx->rbt_distances);
+
+ isc_mem_put(mctx, ctx, sizeof(*ctx));
+}
+
+/*
+ * Walk the tree and ensure that all the test nodes are present.
+ */
+static void
+check_test_data(dns_rbt_t *rbt) {
+ dns_fixedname_t fixed;
+ isc_result_t result;
+ dns_name_t *foundname;
+ size_t i;
+
+ foundname = dns_fixedname_initname(&fixed);
+
+ for (i = 0; i < domain_names_count; i++) {
+ dns_fixedname_t fname;
+ dns_name_t *name;
+ size_t *n;
+
+ dns_test_namefromstring(domain_names[i], &fname);
+
+ name = dns_fixedname_name(&fname);
+ n = NULL;
+ result = dns_rbt_findname(rbt, name, 0, foundname,
+ (void *) &n);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ ATF_CHECK_EQ(*n, i + 1);
+ }
+}
+
+ATF_TC(rbt_create);
+ATF_TC_HEAD(rbt_create, tc) {
+ atf_tc_set_md_var(tc, "descr", "Test the creation of an rbt");
+}
+ATF_TC_BODY(rbt_create, tc) {
+ isc_result_t result;
+ test_context_t *ctx;
+ bool tree_ok;
+
+ UNUSED(tc);
+
+ isc_mem_debugging = ISC_MEM_DEBUGRECORD;
+
+ result = dns_test_begin(NULL, true);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ ctx = test_context_setup();
+
+ check_test_data(ctx->rbt);
+
+ tree_ok = dns__rbt_checkproperties(ctx->rbt);
+ ATF_CHECK_EQ(tree_ok, true);
+
+ test_context_teardown(ctx);
+
+ dns_test_end();
+}
+
+ATF_TC(rbt_nodecount);
+ATF_TC_HEAD(rbt_nodecount, tc) {
+ atf_tc_set_md_var(tc, "descr", "Test dns_rbt_nodecount() on a tree");
+}
+ATF_TC_BODY(rbt_nodecount, tc) {
+ isc_result_t result;
+ test_context_t *ctx;
+
+ UNUSED(tc);
+
+ isc_mem_debugging = ISC_MEM_DEBUGRECORD;
+
+ result = dns_test_begin(NULL, true);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ ctx = test_context_setup();
+
+ ATF_CHECK_EQ(15, dns_rbt_nodecount(ctx->rbt));
+
+ test_context_teardown(ctx);
+
+ dns_test_end();
+}
+
+ATF_TC(rbtnode_get_distance);
+ATF_TC_HEAD(rbtnode_get_distance, tc) {
+ atf_tc_set_md_var(tc, "descr",
+ "Test dns_rbtnode_get_distance() on a tree");
+}
+ATF_TC_BODY(rbtnode_get_distance, tc) {
+ isc_result_t result;
+ test_context_t *ctx;
+ const char *name_str = "a";
+ dns_fixedname_t fname;
+ dns_name_t *name;
+ dns_rbtnode_t *node = NULL;
+ dns_rbtnodechain_t chain;
+
+ UNUSED(tc);
+
+ isc_mem_debugging = ISC_MEM_DEBUGRECORD;
+
+ result = dns_test_begin(NULL, true);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ ctx = test_context_setup();
+
+ dns_test_namefromstring(name_str, &fname);
+ name = dns_fixedname_name(&fname);
+
+ dns_rbtnodechain_init(&chain, mctx);
+
+ result = dns_rbt_findnode(ctx->rbt_distances, name, NULL,
+ &node, &chain, 0, NULL, NULL);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ while (node != NULL) {
+ const size_t *distance = (const size_t *) node->data;
+ if (distance != NULL)
+ ATF_CHECK_EQ(*distance,
+ dns__rbtnode_getdistance(node));
+ result = dns_rbtnodechain_next(&chain, NULL, NULL);
+ if (result == ISC_R_NOMORE)
+ break;
+ dns_rbtnodechain_current(&chain, NULL, NULL, &node);
+ }
+
+ ATF_CHECK_EQ(result, ISC_R_NOMORE);
+
+ dns_rbtnodechain_invalidate(&chain);
+
+ test_context_teardown(ctx);
+
+ dns_test_end();
+}
+
+ATF_TC(rbt_check_distance_random);
+ATF_TC_HEAD(rbt_check_distance_random, tc) {
+ atf_tc_set_md_var(tc, "descr",
+ "Test tree balance, inserting names in random order");
+}
+ATF_TC_BODY(rbt_check_distance_random, tc) {
+ /* This test checks an important performance-related property of
+ * the red-black tree, which is important for us: the longest
+ * path from a sub-tree's root to a node is no more than
+ * 2log(n). This check verifies that the tree is balanced.
+ */
+ dns_rbt_t *mytree = NULL;
+ const unsigned int log_num_nodes = 16;
+
+ int i;
+ isc_result_t result;
+ bool tree_ok;
+
+ UNUSED(tc);
+
+ isc_mem_debugging = ISC_MEM_DEBUGRECORD;
+
+ result = dns_test_begin(NULL, true);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_rbt_create(mctx, delete_data, NULL, &mytree);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ /* Names are inserted in random order. */
+
+ /* Make a large 65536 node top-level domain tree, i.e., the
+ * following code inserts names such as:
+ *
+ * savoucnsrkrqzpkqypbygwoiliawpbmz.
+ * wkadamcbbpjtundbxcmuayuycposvngx.
+ * wzbpznemtooxdpjecdxynsfztvnuyfao.
+ * yueojmhyffslpvfmgyfwioxegfhepnqq.
+ */
+ for (i = 0; i < (1 << log_num_nodes); i++) {
+ size_t *n;
+ char namebuf[34];
+
+ n = isc_mem_get(mctx, sizeof(size_t));
+ ATF_REQUIRE(n != NULL);
+ *n = i + 1;
+
+ while (1) {
+ int j;
+ dns_fixedname_t fname;
+ dns_name_t *name;
+
+ for (j = 0; j < 32; j++) {
+ uint32_t v;
+ isc_random_get(&v);
+ namebuf[j] = 'a' + (v % 26);
+ }
+ namebuf[32] = '.';
+ namebuf[33] = 0;
+
+ dns_test_namefromstring(namebuf, &fname);
+ name = dns_fixedname_name(&fname);
+
+ result = dns_rbt_addname(mytree, name, n);
+ if (result == ISC_R_SUCCESS)
+ break;
+ }
+ }
+
+ /* 1 (root . node) + (1 << log_num_nodes) */
+ ATF_CHECK_EQ(1U + (1U << log_num_nodes), dns_rbt_nodecount(mytree));
+
+ /* The distance from each node to its sub-tree root must be less
+ * than 2 * log(n).
+ */
+ ATF_CHECK((2U * log_num_nodes) >= dns__rbt_getheight(mytree));
+
+ /* Also check RB tree properties */
+ tree_ok = dns__rbt_checkproperties(mytree);
+ ATF_CHECK_EQ(tree_ok, true);
+
+ dns_rbt_destroy(&mytree);
+
+ dns_test_end();
+}
+
+ATF_TC(rbt_check_distance_ordered);
+ATF_TC_HEAD(rbt_check_distance_ordered, tc) {
+ atf_tc_set_md_var(tc, "descr",
+ "Test tree balance, inserting names in sorted order");
+}
+ATF_TC_BODY(rbt_check_distance_ordered, tc) {
+ /* This test checks an important performance-related property of
+ * the red-black tree, which is important for us: the longest
+ * path from a sub-tree's root to a node is no more than
+ * 2log(n). This check verifies that the tree is balanced.
+ */
+ dns_rbt_t *mytree = NULL;
+ const unsigned int log_num_nodes = 16;
+
+ int i;
+ isc_result_t result;
+ bool tree_ok;
+
+ UNUSED(tc);
+
+ isc_mem_debugging = ISC_MEM_DEBUGRECORD;
+
+ result = dns_test_begin(NULL, true);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_rbt_create(mctx, delete_data, NULL, &mytree);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ /* Names are inserted in sorted order. */
+
+ /* Make a large 65536 node top-level domain tree, i.e., the
+ * following code inserts names such as:
+ *
+ * name00000000.
+ * name00000001.
+ * name00000002.
+ * name00000003.
+ */
+ for (i = 0; i < (1 << log_num_nodes); i++) {
+ size_t *n;
+ char namebuf[14];
+ dns_fixedname_t fname;
+ dns_name_t *name;
+
+ n = isc_mem_get(mctx, sizeof(size_t));
+ ATF_REQUIRE(n != NULL);
+ *n = i + 1;
+
+ snprintf(namebuf, sizeof(namebuf), "name%08x.", i);
+ dns_test_namefromstring(namebuf, &fname);
+ name = dns_fixedname_name(&fname);
+
+ result = dns_rbt_addname(mytree, name, n);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ }
+
+ /* 1 (root . node) + (1 << log_num_nodes) */
+ ATF_CHECK_EQ(1U + (1U << log_num_nodes), dns_rbt_nodecount(mytree));
+
+ /* The distance from each node to its sub-tree root must be less
+ * than 2 * log(n).
+ */
+ ATF_CHECK((2U * log_num_nodes) >= dns__rbt_getheight(mytree));
+
+ /* Also check RB tree properties */
+ tree_ok = dns__rbt_checkproperties(mytree);
+ ATF_CHECK_EQ(tree_ok, true);
+
+ dns_rbt_destroy(&mytree);
+
+ dns_test_end();
+}
+
+static isc_result_t
+insert_helper(dns_rbt_t *rbt, const char *namestr, dns_rbtnode_t **node) {
+ dns_fixedname_t fname;
+ dns_name_t *name;
+
+ dns_test_namefromstring(namestr, &fname);
+ name = dns_fixedname_name(&fname);
+
+ return (dns_rbt_addnode(rbt, name, node));
+}
+
+static bool
+compare_labelsequences(dns_rbtnode_t *node, const char *labelstr) {
+ dns_name_t name;
+ isc_result_t result;
+ char *nodestr = NULL;
+ bool is_equal;
+
+ dns_name_init(&name, NULL);
+ dns_rbt_namefromnode(node, &name);
+
+ result = dns_name_tostring(&name, &nodestr, mctx);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ is_equal = strcmp(labelstr, nodestr) == 0 ? true : false;
+
+ isc_mem_free(mctx, nodestr);
+
+ return (is_equal);
+}
+
+ATF_TC(rbt_insert);
+ATF_TC_HEAD(rbt_insert, tc) {
+ atf_tc_set_md_var(tc, "descr", "Test insertion into a tree");
+}
+ATF_TC_BODY(rbt_insert, tc) {
+ isc_result_t result;
+ test_context_t *ctx;
+ dns_rbtnode_t *node;
+
+ UNUSED(tc);
+
+ isc_mem_debugging = ISC_MEM_DEBUGRECORD;
+
+ result = dns_test_begin(NULL, true);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ ctx = test_context_setup();
+
+ /* Check node count before beginning. */
+ ATF_CHECK_EQ(15, dns_rbt_nodecount(ctx->rbt));
+
+ /* Try to insert a node that already exists. */
+ node = NULL;
+ result = insert_helper(ctx->rbt, "d.e.f", &node);
+ ATF_CHECK_EQ(result, ISC_R_EXISTS);
+
+ /* Node count must not have changed. */
+ ATF_CHECK_EQ(15, dns_rbt_nodecount(ctx->rbt));
+
+ /* Try to insert a node that doesn't exist. */
+ node = NULL;
+ result = insert_helper(ctx->rbt, "0", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ ATF_CHECK_EQ(compare_labelsequences(node, "0"), true);
+
+ /* Node count must have increased. */
+ ATF_CHECK_EQ(16, dns_rbt_nodecount(ctx->rbt));
+
+ /* Another. */
+ node = NULL;
+ result = insert_helper(ctx->rbt, "example.com", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ ATF_REQUIRE(node != NULL);
+ ATF_CHECK_EQ(node->data, NULL);
+
+ /* Node count must have increased. */
+ ATF_CHECK_EQ(17, dns_rbt_nodecount(ctx->rbt));
+
+ /* Re-adding it should return EXISTS */
+ node = NULL;
+ result = insert_helper(ctx->rbt, "example.com", &node);
+ ATF_CHECK_EQ(result, ISC_R_EXISTS);
+
+ /* Node count must not have changed. */
+ ATF_CHECK_EQ(17, dns_rbt_nodecount(ctx->rbt));
+
+ /* Fission the node d.e.f */
+ node = NULL;
+ result = insert_helper(ctx->rbt, "k.e.f", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ ATF_CHECK_EQ(compare_labelsequences(node, "k"), true);
+
+ /* Node count must have incremented twice ("d.e.f" fissioned to
+ * "d" and "e.f", and the newly added "k").
+ */
+ ATF_CHECK_EQ(19, dns_rbt_nodecount(ctx->rbt));
+
+ /* Fission the node "g.h" */
+ node = NULL;
+ result = insert_helper(ctx->rbt, "h", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ ATF_CHECK_EQ(compare_labelsequences(node, "h"), true);
+
+ /* Node count must have incremented ("g.h" fissioned to "g" and
+ * "h").
+ */
+ ATF_CHECK_EQ(20, dns_rbt_nodecount(ctx->rbt));
+
+ /* Add child domains */
+
+ node = NULL;
+ result = insert_helper(ctx->rbt, "m.p.w.y.d.e.f", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ ATF_CHECK_EQ(compare_labelsequences(node, "m"), true);
+ ATF_CHECK_EQ(21, dns_rbt_nodecount(ctx->rbt));
+
+ node = NULL;
+ result = insert_helper(ctx->rbt, "n.p.w.y.d.e.f", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ ATF_CHECK_EQ(compare_labelsequences(node, "n"), true);
+ ATF_CHECK_EQ(22, dns_rbt_nodecount(ctx->rbt));
+
+ node = NULL;
+ result = insert_helper(ctx->rbt, "l.a", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ ATF_CHECK_EQ(compare_labelsequences(node, "l"), true);
+ ATF_CHECK_EQ(23, dns_rbt_nodecount(ctx->rbt));
+
+ node = NULL;
+ result = insert_helper(ctx->rbt, "r.d.e.f", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ node = NULL;
+ result = insert_helper(ctx->rbt, "s.d.e.f", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ ATF_CHECK_EQ(25, dns_rbt_nodecount(ctx->rbt));
+
+ node = NULL;
+ result = insert_helper(ctx->rbt, "h.w.y.d.e.f", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ /* Add more nodes one by one to cover left and right rotation
+ * functions.
+ */
+ node = NULL;
+ result = insert_helper(ctx->rbt, "f", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ node = NULL;
+ result = insert_helper(ctx->rbt, "m", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ node = NULL;
+ result = insert_helper(ctx->rbt, "nm", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ node = NULL;
+ result = insert_helper(ctx->rbt, "om", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ node = NULL;
+ result = insert_helper(ctx->rbt, "k", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ node = NULL;
+ result = insert_helper(ctx->rbt, "l", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ node = NULL;
+ result = insert_helper(ctx->rbt, "fe", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ node = NULL;
+ result = insert_helper(ctx->rbt, "ge", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ node = NULL;
+ result = insert_helper(ctx->rbt, "i", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ node = NULL;
+ result = insert_helper(ctx->rbt, "ae", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ node = NULL;
+ result = insert_helper(ctx->rbt, "n", &node);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ test_context_teardown(ctx);
+
+ dns_test_end();
+}
+
+ATF_TC(rbt_remove);
+ATF_TC_HEAD(rbt_remove, tc) {
+ atf_tc_set_md_var(tc, "descr", "Test removal from a tree");
+}
+ATF_TC_BODY(rbt_remove, tc) {
+ /*
+ * This testcase checks that after node removal, the
+ * binary-search tree is valid and all nodes that are supposed
+ * to exist are present in the correct order. It mainly tests
+ * DomainTree as a BST, and not particularly as a red-black
+ * tree. This test checks node deletion when upper nodes have
+ * data.
+ */
+ isc_result_t result;
+ size_t j;
+
+ UNUSED(tc);
+
+ isc_mem_debugging = ISC_MEM_DEBUGRECORD;
+
+ result = dns_test_begin(NULL, true);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ /*
+ * Delete single nodes and check if the rest of the nodes exist.
+ */
+ for (j = 0; j < ordered_names_count; j++) {
+ dns_rbt_t *mytree = NULL;
+ dns_rbtnode_t *node;
+ size_t i;
+ size_t *n;
+ bool tree_ok;
+ dns_rbtnodechain_t chain;
+ size_t start_node;
+
+ /* Create a tree. */
+ result = dns_rbt_create(mctx, delete_data, NULL, &mytree);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ /* Insert test data into the tree. */
+ for (i = 0; i < domain_names_count; i++) {
+ node = NULL;
+ result = insert_helper(mytree, domain_names[i], &node);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ }
+
+ /* Check that all names exist in order. */
+ for (i = 0; i < ordered_names_count; i++) {
+ dns_fixedname_t fname;
+ dns_name_t *name;
+
+ dns_test_namefromstring(ordered_names[i], &fname);
+
+ name = dns_fixedname_name(&fname);
+ node = NULL;
+ result = dns_rbt_findnode(mytree, name, NULL,
+ &node, NULL,
+ DNS_RBTFIND_EMPTYDATA,
+ NULL, NULL);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ /* Add node data */
+ ATF_REQUIRE(node != NULL);
+ ATF_REQUIRE_EQ(node->data, NULL);
+
+ n = isc_mem_get(mctx, sizeof(size_t));
+ ATF_REQUIRE(n != NULL);
+ *n = i;
+
+ node->data = n;
+ }
+
+ /* Now, delete the j'th node from the tree. */
+ {
+ dns_fixedname_t fname;
+ dns_name_t *name;
+
+ dns_test_namefromstring(ordered_names[j], &fname);
+
+ name = dns_fixedname_name(&fname);
+
+ result = dns_rbt_deletename(mytree, name, false);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ }
+
+ /* Check RB tree properties. */
+ tree_ok = dns__rbt_checkproperties(mytree);
+ ATF_CHECK_EQ(tree_ok, true);
+
+ dns_rbtnodechain_init(&chain, mctx);
+
+ /* Now, walk through nodes in order. */
+ if (j == 0) {
+ /*
+ * Node for ordered_names[0] was already deleted
+ * above. We start from node 1.
+ */
+ dns_fixedname_t fname;
+ dns_name_t *name;
+
+ dns_test_namefromstring(ordered_names[0], &fname);
+ name = dns_fixedname_name(&fname);
+ node = NULL;
+ result = dns_rbt_findnode(mytree, name, NULL,
+ &node, NULL,
+ 0,
+ NULL, NULL);
+ ATF_CHECK_EQ(result, ISC_R_NOTFOUND);
+
+ dns_test_namefromstring(ordered_names[1], &fname);
+ name = dns_fixedname_name(&fname);
+ node = NULL;
+ result = dns_rbt_findnode(mytree, name, NULL,
+ &node, &chain,
+ 0,
+ NULL, NULL);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ start_node = 1;
+ } else {
+ /* Start from node 0. */
+ dns_fixedname_t fname;
+ dns_name_t *name;
+
+ dns_test_namefromstring(ordered_names[0], &fname);
+ name = dns_fixedname_name(&fname);
+ node = NULL;
+ result = dns_rbt_findnode(mytree, name, NULL,
+ &node, &chain,
+ 0,
+ NULL, NULL);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ start_node = 0;
+ }
+
+ /*
+ * node and chain have been set by the code above at
+ * this point.
+ */
+ for (i = start_node; i < ordered_names_count; i++) {
+ dns_fixedname_t fname_j, fname_i;
+ dns_name_t *name_j, *name_i;
+
+ dns_test_namefromstring(ordered_names[j], &fname_j);
+ name_j = dns_fixedname_name(&fname_j);
+ dns_test_namefromstring(ordered_names[i], &fname_i);
+ name_i = dns_fixedname_name(&fname_i);
+
+ if (dns_name_equal(name_i, name_j)) {
+ /*
+ * This may be true for the last node if
+ * we seek ahead in the loop using
+ * dns_rbtnodechain_next() below.
+ */
+ if (node == NULL) {
+ break;
+ }
+
+ /* All ordered nodes have data
+ * initially. If any node is empty, it
+ * means it was removed, but an empty
+ * node exists because it is a
+ * super-domain. Just skip it.
+ */
+ if (node->data == NULL) {
+ result = dns_rbtnodechain_next(&chain,
+ NULL,
+ NULL);
+ if (result == ISC_R_NOMORE) {
+ node = NULL;
+ } else {
+ dns_rbtnodechain_current(&chain,
+ NULL,
+ NULL,
+ &node);
+ }
+ }
+ continue;
+ }
+
+ ATF_REQUIRE(node != NULL);
+
+ n = (size_t *) node->data;
+ if (n != NULL) {
+ /* printf("n=%zu, i=%zu\n", *n, i); */
+ ATF_CHECK_EQ(*n, i);
+ }
+
+ result = dns_rbtnodechain_next(&chain, NULL, NULL);
+ if (result == ISC_R_NOMORE) {
+ node = NULL;
+ } else {
+ dns_rbtnodechain_current(&chain, NULL, NULL,
+ &node);
+ }
+ }
+
+ /* We should have reached the end of the tree. */
+ ATF_REQUIRE_EQ(node, NULL);
+
+ dns_rbt_destroy(&mytree);
+ }
+
+ dns_test_end();
+}
+
+static void
+insert_nodes(dns_rbt_t *mytree, char **names,
+ size_t *names_count, uint32_t num_names)
+{
+ uint32_t i;
+ dns_rbtnode_t *node;
+
+ for (i = 0; i < num_names; i++) {
+ size_t *n;
+ char namebuf[34];
+
+ n = isc_mem_get(mctx, sizeof(size_t));
+ ATF_REQUIRE(n != NULL);
+
+ *n = i; /* Unused value */
+
+ while (1) {
+ int j;
+ dns_fixedname_t fname;
+ dns_name_t *name;
+ isc_result_t result;
+
+ for (j = 0; j < 32; j++) {
+ uint32_t v;
+ isc_random_get(&v);
+ namebuf[j] = 'a' + (v % 26);
+ }
+ namebuf[32] = '.';
+ namebuf[33] = 0;
+
+ dns_test_namefromstring(namebuf, &fname);
+ name = dns_fixedname_name(&fname);
+
+ node = NULL;
+ result = dns_rbt_addnode(mytree, name, &node);
+ if (result == ISC_R_SUCCESS) {
+ node->data = n;
+ names[*names_count] = isc_mem_strdup(mctx,
+ namebuf);
+ ATF_REQUIRE(names[*names_count] != NULL);
+ *names_count += 1;
+ break;
+ }
+ }
+ }
+}
+
+static void
+remove_nodes(dns_rbt_t *mytree, char **names,
+ size_t *names_count, uint32_t num_names)
+{
+ uint32_t i;
+
+ UNUSED(mytree);
+
+ for (i = 0; i < num_names; i++) {
+ uint32_t node;
+ dns_fixedname_t fname;
+ dns_name_t *name;
+ isc_result_t result;
+
+ isc_random_get(&node);
+
+ node %= *names_count;
+
+ dns_test_namefromstring(names[node], &fname);
+ name = dns_fixedname_name(&fname);
+
+ result = dns_rbt_deletename(mytree, name, false);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ isc_mem_free(mctx, names[node]);
+ if (*names_count > 0) {
+ names[node] = names[*names_count - 1];
+ names[*names_count - 1] = NULL;
+ *names_count -= 1;
+ }
+ }
+}
+
+static void
+check_tree(dns_rbt_t *mytree, char **names, size_t names_count,
+ unsigned int line)
+{
+ bool tree_ok;
+
+ UNUSED(names);
+
+ ATF_CHECK_EQ_MSG(names_count + 1, dns_rbt_nodecount(mytree),
+ "line:%u: %lu != %u", line,
+ (unsigned long)(names_count + 1),
+ dns_rbt_nodecount(mytree));
+
+ /*
+ * The distance from each node to its sub-tree root must be less
+ * than 2 * log_2(1024).
+ */
+ ATF_CHECK((2 * 10) >= dns__rbt_getheight(mytree));
+
+ /* Also check RB tree properties */
+ tree_ok = dns__rbt_checkproperties(mytree);
+ ATF_CHECK_EQ(tree_ok, true);
+}
+
+ATF_TC(rbt_insert_and_remove);
+ATF_TC_HEAD(rbt_insert_and_remove, tc) {
+ atf_tc_set_md_var(tc, "descr",
+ "Test insert and remove in a loop");
+}
+ATF_TC_BODY(rbt_insert_and_remove, tc) {
+ /*
+ * What is the best way to test our red-black tree code? It is
+ * not a good method to test every case handled in the actual
+ * code itself. This is because our approach itself may be
+ * incorrect.
+ *
+ * We test our code at the interface level here by exercising the
+ * tree randomly multiple times, checking that red-black tree
+ * properties are valid, and all the nodes that are supposed to be
+ * in the tree exist and are in order.
+ *
+ * NOTE: These tests are run within a single tree level in the
+ * forest. The number of nodes in the tree level doesn't grow
+ * over 1024.
+ */
+ isc_result_t result;
+ dns_rbt_t *mytree = NULL;
+ size_t *n;
+ /*
+ * We use an array for storing names instead of a set
+ * structure. It's slow, but works and is good enough for tests.
+ */
+ char *names[1024];
+ size_t names_count;
+ int i;
+
+ UNUSED(tc);
+
+ isc_mem_debugging = ISC_MEM_DEBUGRECORD;
+
+ result = dns_test_begin(NULL, true);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_rbt_create(mctx, delete_data, NULL, &mytree);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ n = isc_mem_get(mctx, sizeof(size_t));
+ ATF_REQUIRE(n != NULL);
+ result = dns_rbt_addname(mytree, dns_rootname, n);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ memset(names, 0, sizeof(names));
+ names_count = 0;
+
+ /* Repeat the insert/remove test some 4096 times */
+ for (i = 0; i < 4096; i++) {
+ uint32_t num_names;
+ isc_random_get(&num_names);
+
+ if (names_count < 1024) {
+ num_names %= 1024 - names_count;
+ num_names++;
+ } else {
+ num_names = 0;
+ }
+
+ insert_nodes(mytree, names, &names_count, num_names);
+ check_tree(mytree, names, names_count, __LINE__);
+
+ isc_random_get(&num_names);
+ if (names_count > 0) {
+ num_names %= names_count;
+ num_names++;
+ } else {
+ num_names = 0;
+ }
+
+ remove_nodes(mytree, names, &names_count, num_names);
+ check_tree(mytree, names, names_count, __LINE__);
+ }
+
+ /* Remove the rest of the nodes */
+ remove_nodes(mytree, names, &names_count, names_count);
+ check_tree(mytree, names, names_count, __LINE__);
+
+ for (i = 0; i < 1024; i++) {
+ if (names[i] != NULL) {
+ isc_mem_free(mctx, names[i]);
+ }
+ }
+
+ result = dns_rbt_deletename(mytree, dns_rootname, false);
+ ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS,
+ "result: %s", isc_result_totext(result));
+ ATF_CHECK_EQ_MSG(dns_rbt_nodecount(mytree), 0,
+ "%u != 0", dns_rbt_nodecount(mytree));
+
+ dns_rbt_destroy(&mytree);
+
+ dns_test_end();
+}
+
+ATF_TC(rbt_findname);
+ATF_TC_HEAD(rbt_findname, tc) {
+ atf_tc_set_md_var(tc, "descr", "findname return values");
+}
+ATF_TC_BODY(rbt_findname, tc) {
+ isc_result_t result;
+ test_context_t *ctx = NULL;
+ dns_fixedname_t fname, found;
+ dns_name_t *name = NULL, *foundname = NULL;
+ size_t *n = NULL;
+
+ UNUSED(tc);
+
+ isc_mem_debugging = ISC_MEM_DEBUGRECORD;
+
+ result = dns_test_begin(NULL, true);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ ctx = test_context_setup();
+
+ /* Try to find a name that exists. */
+ dns_test_namefromstring("d.e.f", &fname);
+ name = dns_fixedname_name(&fname);
+
+ foundname = dns_fixedname_initname(&found);
+
+ result = dns_rbt_findname(ctx->rbt, name,
+ DNS_RBTFIND_EMPTYDATA,
+ foundname, (void *) &n);
+ ATF_CHECK(dns_name_equal(foundname, name));
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ /* Now without EMPTYDATA */
+ result = dns_rbt_findname(ctx->rbt, name, 0,
+ foundname, (void *) &n);
+ ATF_CHECK_EQ(result, ISC_R_NOTFOUND);
+
+ /* Now one that partially matches */
+ dns_test_namefromstring("d.e.f.g.h.i.j", &fname);
+ name = dns_fixedname_name(&fname);
+ result = dns_rbt_findname(ctx->rbt, name,
+ DNS_RBTFIND_EMPTYDATA,
+ foundname, (void *) &n);
+ ATF_CHECK_EQ(result, DNS_R_PARTIALMATCH);
+
+ /* Now one that doesn't match */
+ dns_test_namefromstring("1.2", &fname);
+ name = dns_fixedname_name(&fname);
+ result = dns_rbt_findname(ctx->rbt, name,
+ DNS_RBTFIND_EMPTYDATA,
+ foundname, (void *) &n);
+ ATF_CHECK_EQ(result, DNS_R_PARTIALMATCH);
+ ATF_CHECK(dns_name_equal(foundname, dns_rootname));
+
+ test_context_teardown(ctx);
+
+ dns_test_end();
+}
+
+ATF_TC(rbt_addname);
+ATF_TC_HEAD(rbt_addname, tc) {
+ atf_tc_set_md_var(tc, "descr", "addname return values");
+}
+ATF_TC_BODY(rbt_addname, tc) {
+ isc_result_t result;
+ test_context_t *ctx = NULL;
+ dns_fixedname_t fname;
+ dns_name_t *name = NULL;
+ size_t *n;
+
+ UNUSED(tc);
+
+ isc_mem_debugging = ISC_MEM_DEBUGRECORD;
+
+ result = dns_test_begin(NULL, true);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ ctx = test_context_setup();
+
+ n = isc_mem_get(mctx, sizeof(size_t));
+ ATF_REQUIRE(n != NULL);
+ *n = 1;
+
+ dns_test_namefromstring("d.e.f.g.h.i.j.k", &fname);
+ name = dns_fixedname_name(&fname);
+
+ /* Add a name that doesn't exist */
+ result = dns_rbt_addname(ctx->rbt, name, n);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ /* Now add again, should get ISC_R_EXISTS */
+ n = isc_mem_get(mctx, sizeof(size_t));
+ ATF_REQUIRE(n != NULL);
+ *n = 2;
+ result = dns_rbt_addname(ctx->rbt, name, n);
+ ATF_REQUIRE_EQ(result, ISC_R_EXISTS);
+ isc_mem_put(mctx, n, sizeof(size_t));
+
+ test_context_teardown(ctx);
+
+ dns_test_end();
+}
+
+ATF_TC(rbt_deletename);
+ATF_TC_HEAD(rbt_deletename, tc) {
+ atf_tc_set_md_var(tc, "descr", "deletename return values");
+}
+ATF_TC_BODY(rbt_deletename, tc) {
+ isc_result_t result;
+ test_context_t *ctx = NULL;
+ dns_fixedname_t fname;
+ dns_name_t *name = NULL;
+
+ UNUSED(tc);
+
+ isc_mem_debugging = ISC_MEM_DEBUGRECORD;
+
+ result = dns_test_begin(NULL, true);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ ctx = test_context_setup();
+
+ /* Delete a name that doesn't exist */
+ dns_test_namefromstring("z.x.y.w", &fname);
+ name = dns_fixedname_name(&fname);
+ result = dns_rbt_deletename(ctx->rbt, name, false);
+ ATF_REQUIRE_EQ(result, ISC_R_NOTFOUND);
+
+ /* Now one that does */
+ dns_test_namefromstring("d.e.f", &fname);
+ name = dns_fixedname_name(&fname);
+ result = dns_rbt_deletename(ctx->rbt, name, false);
+ ATF_REQUIRE_EQ(result, ISC_R_NOTFOUND);
+
+ test_context_teardown(ctx);
+
+ dns_test_end();
+}
+
+ATF_TC(rbt_nodechain);
+ATF_TC_HEAD(rbt_nodechain, tc) {
+ atf_tc_set_md_var(tc, "descr", "nodechain");
+}
+ATF_TC_BODY(rbt_nodechain, tc) {
+ isc_result_t result;
+ test_context_t *ctx;
+ dns_fixedname_t fname, found, expect;
+ dns_name_t *name, *foundname, *expected;
+ dns_rbtnode_t *node = NULL;
+ dns_rbtnodechain_t chain;
+
+ UNUSED(tc);
+
+ isc_mem_debugging = ISC_MEM_DEBUGRECORD;
+
+ result = dns_test_begin(NULL, true);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ ctx = test_context_setup();
+
+ dns_rbtnodechain_init(&chain, mctx);
+
+ dns_test_namefromstring("a", &fname);
+ name = dns_fixedname_name(&fname);
+
+ result = dns_rbt_findnode(ctx->rbt, name, NULL,
+ &node, &chain, 0, NULL, NULL);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ foundname = dns_fixedname_initname(&found);
+
+ dns_test_namefromstring("a", &expect);
+ expected = dns_fixedname_name(&expect);
+ UNUSED(expected);
+
+ result = dns_rbtnodechain_first(&chain, ctx->rbt, foundname, NULL);
+ ATF_CHECK_EQ(result, DNS_R_NEWORIGIN);
+ ATF_CHECK_EQ(dns_name_countlabels(foundname), 0);
+
+ result = dns_rbtnodechain_prev(&chain, NULL, NULL);
+ ATF_CHECK_EQ(result, ISC_R_NOMORE);
+
+ result = dns_rbtnodechain_next(&chain, NULL, NULL);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_rbtnodechain_next(&chain, NULL, NULL);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_rbtnodechain_last(&chain, ctx->rbt, NULL, NULL);
+ ATF_CHECK_EQ(result, DNS_R_NEWORIGIN);
+
+ result = dns_rbtnodechain_next(&chain, NULL, NULL);
+ ATF_CHECK_EQ(result, ISC_R_NOMORE);
+
+ result = dns_rbtnodechain_last(&chain, ctx->rbt, NULL, NULL);
+ ATF_CHECK_EQ(result, DNS_R_NEWORIGIN);
+
+ result = dns_rbtnodechain_prev(&chain, NULL, NULL);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ dns_rbtnodechain_invalidate(&chain);
+
+ test_context_teardown(ctx);
+
+ dns_test_end();
+}
+
+#ifdef ISC_PLATFORM_USETHREADS
+#ifdef DNS_BENCHMARK_TESTS
+
+/*
+ * XXXMUKS: Don't delete this code. It is useful in benchmarking the
+ * RBT, but we don't require it as part of the unit test runs.
+ */
+
+ATF_TC(benchmark);
+ATF_TC_HEAD(benchmark, tc) {
+ atf_tc_set_md_var(tc, "descr", "Benchmark RBT implementation");
+}
+
+static dns_fixedname_t *fnames;
+static dns_name_t **names;
+static int *values;
+
+static void *
+find_thread(void *arg) {
+ dns_rbt_t *mytree;
+ isc_result_t result;
+ dns_rbtnode_t *node;
+ unsigned int j, i;
+ unsigned int start = 0;
+
+ mytree = (dns_rbt_t *) arg;
+ while (start == 0)
+ start = random() % 4000000;
+
+ /* Query 32 million random names from it in each thread */
+ for (j = 0; j < 8; j++) {
+ for (i = start; i != start - 1; i = (i + 1) % 4000000) {
+ node = NULL;
+ result = dns_rbt_findnode(mytree, names[i], NULL,
+ &node, NULL,
+ DNS_RBTFIND_EMPTYDATA,
+ NULL, NULL);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ ATF_REQUIRE(node != NULL);
+ ATF_CHECK_EQ(values[i], (intptr_t) node->data);
+ }
+ }
+
+ return (NULL);
+}
+
+ATF_TC_BODY(benchmark, tc) {
+ isc_result_t result;
+ char namestr[sizeof("name18446744073709551616.example.org.")];
+ unsigned int r;
+ dns_rbt_t *mytree;
+ dns_rbtnode_t *node;
+ unsigned int i;
+ unsigned int maxvalue = 1000000;
+ isc_time_t ts1, ts2;
+ double t;
+ unsigned int nthreads;
+ isc_thread_t threads[32];
+
+ UNUSED(tc);
+
+ srandom(time(NULL));
+
+ debug_mem_record = false;
+
+ result = dns_test_begin(NULL, true);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ fnames = (dns_fixedname_t *) malloc(4000000 * sizeof(dns_fixedname_t));
+ names = (dns_name_t **) malloc(4000000 * sizeof(dns_name_t *));
+ values = (int *) malloc(4000000 * sizeof(int));
+
+ for (i = 0; i < 4000000; i++) {
+ r = ((unsigned long) random()) % maxvalue;
+ snprintf(namestr, sizeof(namestr), "name%u.example.org.", r);
+ dns_test_namefromstring(namestr, &fnames[i]);
+ names[i] = dns_fixedname_name(&fnames[i]);
+ values[i] = r;
+ }
+
+ /* Create a tree. */
+ mytree = NULL;
+ result = dns_rbt_create(mctx, NULL, NULL, &mytree);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ /* Insert test data into the tree. */
+ for (i = 0; i < maxvalue; i++) {
+ snprintf(namestr, sizeof(namestr), "name%u.example.org.", i);
+ node = NULL;
+ result = insert_helper(mytree, namestr, &node);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ node->data = (void *) (intptr_t) i;
+ }
+
+ result = isc_time_now(&ts1);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ nthreads = ISC_MIN(isc_os_ncpus(), 32);
+ nthreads = ISC_MAX(nthreads, 1);
+ for (i = 0; i < nthreads; i++) {
+ result = isc_thread_create(find_thread, mytree, &threads[i]);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ }
+
+ for (i = 0; i < nthreads; i++) {
+ result = isc_thread_join(threads[i], NULL);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ }
+
+ result = isc_time_now(&ts2);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ t = isc_time_microdiff(&ts2, &ts1);
+
+ printf("%u findnode calls, %f seconds, %f calls/second\n",
+ nthreads * 8 * 4000000, t / 1000000.0,
+ (nthreads * 8 * 4000000) / (t / 1000000.0));
+
+ free(values);
+ free(names);
+ free(fnames);
+
+ dns_rbt_destroy(&mytree);
+
+ dns_test_end();
+}
+
+#endif /* DNS_BENCHMARK_TESTS */
+#endif /* ISC_PLATFORM_USETHREADS */
+
+/*
+ * Main
+ */
+ATF_TP_ADD_TCS(tp) {
+ ATF_TP_ADD_TC(tp, rbt_create);
+ ATF_TP_ADD_TC(tp, rbt_nodecount);
+ ATF_TP_ADD_TC(tp, rbtnode_get_distance);
+ ATF_TP_ADD_TC(tp, rbt_check_distance_random);
+ ATF_TP_ADD_TC(tp, rbt_check_distance_ordered);
+ ATF_TP_ADD_TC(tp, rbt_insert);
+ ATF_TP_ADD_TC(tp, rbt_remove);
+ ATF_TP_ADD_TC(tp, rbt_insert_and_remove);
+ ATF_TP_ADD_TC(tp, rbt_findname);
+ ATF_TP_ADD_TC(tp, rbt_addname);
+ ATF_TP_ADD_TC(tp, rbt_deletename);
+ ATF_TP_ADD_TC(tp, rbt_nodechain);
+#ifdef ISC_PLATFORM_USETHREADS
+#ifdef DNS_BENCHMARK_TESTS
+ ATF_TP_ADD_TC(tp, benchmark);
+#endif /* DNS_BENCHMARK_TESTS */
+#endif /* ISC_PLATFORM_USETHREADS */
+
+ return (atf_no_error());
+}
diff --git a/lib/dns/tests/rdata_test.c b/lib/dns/tests/rdata_test.c
new file mode 100644
index 0000000..6837516
--- /dev/null
+++ b/lib/dns/tests/rdata_test.c
@@ -0,0 +1,1214 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <atf-c.h>
+
+#include <stdbool.h>
+#include <unistd.h>
+
+#include <isc/hex.h>
+#include <isc/lex.h>
+#include <isc/types.h>
+
+#include <dns/callbacks.h>
+#include <dns/rdata.h>
+
+#include "dnstest.h"
+
+/*****
+ ***** Commonly used structures
+ *****/
+
+/*
+ * An array of these structures is passed to check_text_ok().
+ */
+struct text_ok {
+ const char *text_in; /* text passed to fromtext_*() */
+ const char *text_out; /* text expected from totext_*();
+ NULL indicates text_in is invalid */
+ int lineno; /* source line defining this RDATA */
+};
+typedef struct text_ok text_ok_t;
+
+/*
+ * An array of these structures is passed to check_wire_ok().
+ */
+struct wire_ok {
+ unsigned char data[512]; /* RDATA in wire format */
+ size_t len; /* octets of data to parse */
+ bool ok; /* is this RDATA valid? */
+ int lineno; /* source line defining this RDATA */
+};
+typedef struct wire_ok wire_ok_t;
+
+/*****
+ ***** Convenience macros for creating the above structures
+ *****/
+
+#define TEXT_VALID_CHANGED(data_in, data_out) \
+ { data_in, data_out, __LINE__ }
+#define TEXT_VALID(data) { data, data, __LINE__ }
+#define TEXT_INVALID(data) { data, NULL, __LINE__ }
+#define TEXT_SENTINEL() TEXT_INVALID(NULL)
+
+#define VARGC(...) (sizeof((unsigned char[]){ __VA_ARGS__ }))
+#define WIRE_TEST(ok, ...) { \
+ { __VA_ARGS__ }, VARGC(__VA_ARGS__), \
+ ok, __LINE__ \
+ }
+#define WIRE_VALID(...) WIRE_TEST(true, __VA_ARGS__)
+#define WIRE_INVALID(...) WIRE_TEST(false, __VA_ARGS__)
+#define WIRE_SENTINEL() WIRE_TEST(false)
+
+/*****
+ ***** Checking functions used by test cases
+ *****/
+
+/*
+ * Test whether converting rdata to a type-specific struct and then back to
+ * rdata results in the same uncompressed wire form. This checks whether
+ * tostruct_*() and fromstruct_*() routines for given RR class and type behave
+ * consistently.
+ *
+ * This function is called for every correctly processed input RDATA, from both
+ * check_text_ok_single() and check_wire_ok_single().
+ */
+static void
+check_struct_conversions(dns_rdata_t *rdata, size_t structsize, int lineno) {
+ dns_rdataclass_t rdclass = rdata->rdclass;
+ dns_rdatatype_t type = rdata->type;
+ isc_result_t result;
+ isc_buffer_t target;
+ void *rdata_struct;
+ char buf[1024], hex[BUFSIZ];
+
+ rdata_struct = isc_mem_allocate(mctx, structsize);
+ ATF_REQUIRE(rdata_struct != NULL);
+
+ /*
+ * Convert from uncompressed wire form into type-specific struct.
+ */
+ result = dns_rdata_tostruct(rdata, rdata_struct, NULL);
+ ATF_REQUIRE_EQ_MSG(result, ISC_R_SUCCESS,
+ "%s (%u): dns_rdata_tostruct() failed",
+ dns_test_tohex(rdata->data, rdata->length,
+ hex, sizeof(hex)),
+ rdata->length);
+ /*
+ * Convert from type-specific struct into uncompressed wire form.
+ */
+ isc_buffer_init(&target, buf, sizeof(buf));
+ result = dns_rdata_fromstruct(NULL, rdclass, type, rdata_struct,
+ &target);
+ ATF_REQUIRE_EQ_MSG(result, ISC_R_SUCCESS,
+ "line %d: %s (%u): dns_rdata_fromstruct() failed",
+ lineno, dns_test_tohex(rdata->data, rdata->length,
+ hex, sizeof(hex)),
+ rdata->length);
+ /*
+ * Ensure results are consistent.
+ */
+ ATF_REQUIRE_EQ_MSG(isc_buffer_usedlength(&target), rdata->length,
+ "line %d: %s (%u): wire form data length changed "
+ "after converting to type-specific struct and back",
+ lineno, dns_test_tohex(rdata->data, rdata->length,
+ hex, sizeof(hex)),
+ rdata->length);
+ ATF_REQUIRE_EQ_MSG(memcmp(buf, rdata->data, rdata->length), 0,
+ "line %d: %s (%u): wire form data different after "
+ "converting to type-specific struct and back",
+ lineno, dns_test_tohex(rdata->data, rdata->length,
+ hex, sizeof(hex)),
+ rdata->length);
+
+ isc_mem_free(mctx, rdata_struct);
+}
+
+/*
+ * Check whether converting supplied text form RDATA into uncompressed wire
+ * form succeeds (tests fromtext_*()). If so, try converting it back into text
+ * form and see if it results in the original text (tests totext_*()).
+ */
+static void
+check_text_ok_single(const text_ok_t *text_ok, dns_rdataclass_t rdclass,
+ dns_rdatatype_t type, size_t structsize)
+{
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ unsigned char buf_fromtext[1024];
+ char buf_totext[1024] = { 0 };
+ isc_buffer_t target;
+ isc_result_t result;
+
+ /*
+ * Try converting text form RDATA into uncompressed wire form.
+ */
+ result = dns_test_rdatafromstring(&rdata, rdclass, type, buf_fromtext,
+ sizeof(buf_fromtext),
+ text_ok->text_in);
+ /*
+ * Check whether result is as expected.
+ */
+ if (text_ok->text_out != NULL) {
+ ATF_REQUIRE_EQ_MSG(result, ISC_R_SUCCESS,
+ "line %d: '%s': "
+ "expected success, got failure",
+ text_ok->lineno, text_ok->text_in);
+ } else {
+ ATF_REQUIRE_MSG(result != ISC_R_SUCCESS,
+ "line %d: '%s': "
+ "expected failure, got success",
+ text_ok->lineno, text_ok->text_in);
+ }
+ /*
+ * If text form RDATA was not parsed correctly, performing any
+ * additional checks is pointless.
+ */
+ if (result != ISC_R_SUCCESS) {
+ return;
+ }
+ /*
+ * Try converting uncompressed wire form RDATA back into text form and
+ * check whether the resulting text is the same as the original one.
+ */
+ isc_buffer_init(&target, buf_totext, sizeof(buf_totext));
+ result = dns_rdata_totext(&rdata, NULL, &target);
+ ATF_REQUIRE_EQ_MSG(result, ISC_R_SUCCESS,
+ "line %d: '%s': "
+ "failed to convert rdata back to text form",
+ text_ok->lineno, text_ok->text_in);
+ ATF_REQUIRE_EQ_MSG(strcmp(buf_totext, text_ok->text_out), 0,
+ "line %d: '%s': "
+ "converts back to '%s', expected '%s'",
+ text_ok->lineno, text_ok->text_in, buf_totext,
+ text_ok->text_out);
+ /*
+ * Perform two-way conversion checks between uncompressed wire form and
+ * type-specific struct.
+ */
+ check_struct_conversions(&rdata, structsize, text_ok->lineno);
+}
+
+/*
+ * Test whether supplied wire form RDATA is properly handled as being either
+ * valid or invalid for an RR of given rdclass and type.
+ */
+static void
+check_wire_ok_single(const wire_ok_t *wire_ok, dns_rdataclass_t rdclass,
+ dns_rdatatype_t type, size_t structsize)
+{
+ isc_buffer_t source, target;
+ unsigned char buf[1024];
+ dns_decompress_t dctx;
+ isc_result_t result;
+ dns_rdata_t rdata;
+ char hex[BUFSIZ];
+
+ /*
+ * Set up len-octet buffer pointing at data.
+ */
+ isc_buffer_constinit(&source, wire_ok->data, wire_ok->len);
+ isc_buffer_add(&source, wire_ok->len);
+ isc_buffer_setactive(&source, wire_ok->len);
+ /*
+ * Initialize target structures.
+ */
+ isc_buffer_init(&target, buf, sizeof(buf));
+ dns_rdata_init(&rdata);
+ /*
+ * Try converting wire data into uncompressed wire form.
+ */
+ dns_decompress_init(&dctx, -1, DNS_DECOMPRESS_ANY);
+ result = dns_rdata_fromwire(&rdata, rdclass, type, &source, &dctx, 0,
+ &target);
+ dns_decompress_invalidate(&dctx);
+ /*
+ * Check whether result is as expected.
+ */
+ if (wire_ok->ok) {
+ ATF_REQUIRE_EQ_MSG(result, ISC_R_SUCCESS,
+ "line %d: %s (%lu): "
+ "expected success, got failure",
+ wire_ok->lineno,
+ dns_test_tohex(wire_ok->data, wire_ok->len,
+ hex, sizeof(hex)),
+ (unsigned long)wire_ok->len);
+ } else {
+ ATF_REQUIRE_MSG(result != ISC_R_SUCCESS,
+ "line %d: %s (%lu): "
+ "expected failure, got success",
+ wire_ok->lineno,
+ dns_test_tohex(wire_ok->data, wire_ok->len,
+ hex, sizeof(hex)),
+ (unsigned long)wire_ok->len);
+ }
+ /*
+ * If data was parsed correctly, perform two-way conversion checks
+ * between uncompressed wire form and type-specific struct.
+ */
+ if (result == ISC_R_SUCCESS) {
+ check_struct_conversions(&rdata, structsize, wire_ok->lineno);
+ }
+}
+
+/*
+ * Test fromtext_*() and totext_*() routines for given RR class and type for
+ * each text form RDATA in the supplied array. See the comment for
+ * check_text_ok_single() for an explanation of how exactly these routines are
+ * tested.
+ */
+static void
+check_text_ok(const text_ok_t *text_ok, dns_rdataclass_t rdclass,
+ dns_rdatatype_t type, size_t structsize)
+{
+ size_t i;
+
+ /*
+ * Check all entries in the supplied array.
+ */
+ for (i = 0; text_ok[i].text_in != NULL; i++) {
+ check_text_ok_single(&text_ok[i], rdclass, type, structsize);
+ }
+}
+
+/*
+ * For each wire form RDATA in the supplied array, check whether it is properly
+ * handled as being either valid or invalid for an RR of given rdclass and
+ * type, then check whether trying to process a zero-length wire data buffer
+ * yields the expected result. This checks whether the fromwire_*() routine
+ * for given RR class and type behaves as expected.
+ */
+static void
+check_wire_ok(const wire_ok_t *wire_ok, bool empty_ok,
+ dns_rdataclass_t rdclass, dns_rdatatype_t type,
+ size_t structsize)
+{
+ wire_ok_t empty_wire = WIRE_TEST(empty_ok);
+ size_t i;
+
+ /*
+ * Check all entries in the supplied array.
+ */
+ for (i = 0; wire_ok[i].len != 0; i++) {
+ check_wire_ok_single(&wire_ok[i], rdclass, type, structsize);
+ }
+
+ /*
+ * Check empty wire data.
+ */
+ check_wire_ok_single(&empty_wire, rdclass, type, structsize);
+}
+
+/*
+ * Test whether supplied sets of text form and/or wire form RDATA are handled
+ * as expected. This is just a helper function which should be the only
+ * function called for a test case using it, due to the use of dns_test_begin()
+ * and dns_test_end().
+ *
+ * The empty_ok argument denotes whether an attempt to parse a zero-length wire
+ * data buffer should succeed or not (it is valid for some RR types). There is
+ * no point in performing a similar check for empty text form RDATA, because
+ * dns_rdata_fromtext() returns ISC_R_UNEXPECTEDEND before calling fromtext_*()
+ * for the given RR class and type.
+ */
+static void
+check_rdata(const text_ok_t *text_ok, const wire_ok_t *wire_ok,
+ bool empty_ok, dns_rdataclass_t rdclass,
+ dns_rdatatype_t type, size_t structsize)
+{
+ isc_result_t result;
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ if (text_ok != NULL) {
+ check_text_ok(text_ok, rdclass, type, structsize);
+ }
+ if (wire_ok != NULL) {
+ check_wire_ok(wire_ok, empty_ok, rdclass, type, structsize);
+ }
+
+ dns_test_end();
+}
+
+/*****
+ ***** Individual unit tests
+ *****/
+
+/*
+ * CSYNC tests.
+ *
+ * RFC 7477:
+ *
+ * 2.1. The CSYNC Resource Record Format
+ *
+ * 2.1.1. The CSYNC Resource Record Wire Format
+ *
+ * The CSYNC RDATA consists of the following fields:
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SOA Serial |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Flags | Type Bit Map /
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * / Type Bit Map (continued) /
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * 2.1.1.1. The SOA Serial Field
+ *
+ * The SOA Serial field contains a copy of the 32-bit SOA serial number
+ * from the child zone. If the soaminimum flag is set, parental agents
+ * querying children's authoritative servers MUST NOT act on data from
+ * zones advertising an SOA serial number less than this value. See
+ * [RFC1982] for properly implementing "less than" logic. If the
+ * soaminimum flag is not set, parental agents MUST ignore the value in
+ * the SOA Serial field. Clients can set the field to any value if the
+ * soaminimum flag is unset, such as the number zero.
+ *
+ * (...)
+ *
+ * 2.1.1.2. The Flags Field
+ *
+ * The Flags field contains 16 bits of boolean flags that define
+ * operations that affect the processing of the CSYNC record. The flags
+ * defined in this document are as follows:
+ *
+ * 0x00 0x01: "immediate"
+ *
+ * 0x00 0x02: "soaminimum"
+ *
+ * The definitions for how the flags are to be used can be found in
+ * Section 3.
+ *
+ * The remaining flags are reserved for use by future specifications.
+ * Undefined flags MUST be set to 0 by CSYNC publishers. Parental
+ * agents MUST NOT process a CSYNC record if it contains a 1 value for a
+ * flag that is unknown to or unsupported by the parental agent.
+ *
+ * 2.1.1.2.1. The Type Bit Map Field
+ *
+ * The Type Bit Map field indicates the record types to be processed by
+ * the parental agent, according to the procedures in Section 3. The
+ * Type Bit Map field is encoded in the same way as the Type Bit Map
+ * field of the NSEC record, described in [RFC4034], Section 4.1.2. If
+ * a bit has been set that a parental agent implementation does not
+ * understand, the parental agent MUST NOT act upon the record.
+ * Specifically, a parental agent must not simply copy the data, and it
+ * must understand the semantics associated with a bit in the Type Bit
+ * Map field that has been set to 1.
+ */
+ATF_TC(csync);
+ATF_TC_HEAD(csync, tc) {
+ atf_tc_set_md_var(tc, "descr", "CSYNC RDATA manipulations");
+}
+ATF_TC_BODY(csync, tc) {
+ text_ok_t text_ok[] = {
+ TEXT_INVALID(""),
+ TEXT_INVALID("0"),
+ TEXT_VALID("0 0"),
+ TEXT_VALID("0 0 A"),
+ TEXT_VALID("0 0 NS"),
+ TEXT_VALID("0 0 AAAA"),
+ TEXT_VALID("0 0 A AAAA"),
+ TEXT_VALID("0 0 A NS AAAA"),
+ TEXT_INVALID("0 0 A NS AAAA BOGUS"),
+ TEXT_SENTINEL()
+ };
+ wire_ok_t wire_ok[] = {
+ /*
+ * Short.
+ */
+ WIRE_INVALID(0x00),
+ /*
+ * Short.
+ */
+ WIRE_INVALID(0x00, 0x00),
+ /*
+ * Short.
+ */
+ WIRE_INVALID(0x00, 0x00, 0x00),
+ /*
+ * Short.
+ */
+ WIRE_INVALID(0x00, 0x00, 0x00, 0x00),
+ /*
+ * Short.
+ */
+ WIRE_INVALID(0x00, 0x00, 0x00, 0x00, 0x00),
+ /*
+ * Serial + flags only.
+ */
+ WIRE_VALID(0x00, 0x00, 0x00, 0x00, 0x00, 0x00),
+ /*
+ * Bad type map.
+ */
+ WIRE_INVALID(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00),
+ /*
+ * Bad type map.
+ */
+ WIRE_INVALID(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00),
+ /*
+ * Good type map.
+ */
+ WIRE_VALID(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x02),
+ /*
+ * Sentinel.
+ */
+ WIRE_SENTINEL()
+ };
+
+ UNUSED(tc);
+
+ check_rdata(text_ok, wire_ok, false, dns_rdataclass_in,
+ dns_rdatatype_csync, sizeof(dns_rdata_csync_t));
+}
+
+/*
+ * DOA tests.
+ *
+ * draft-durand-doa-over-dns-03:
+ *
+ * 3.2. DOA RDATA Wire Format
+ *
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * 0: | |
+ * | DOA-ENTERPRISE |
+ * | |
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * 4: | |
+ * | DOA-TYPE |
+ * | |
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * 8: | DOA-LOCATION | DOA-MEDIA-TYPE /
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * 10: / /
+ * / DOA-MEDIA-TYPE (continued) /
+ * / /
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * / /
+ * / DOA-DATA /
+ * / /
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ *
+ * DOA-ENTERPRISE: a 32-bit unsigned integer in network order.
+ *
+ * DOA-TYPE: a 32-bit unsigned integer in network order.
+ *
+ * DOA-LOCATION: an 8-bit unsigned integer.
+ *
+ * DOA-MEDIA-TYPE: A <character-string> (see [RFC1035]). The first
+ * octet of the <character-string> contains the number of characters to
+ * follow.
+ *
+ * DOA-DATA: A variable length blob of binary data. The length of the
+ * DOA-DATA is not contained within the wire format of the RR and has to
+ * be computed from the RDLENGTH of the entire RR once other fields have
+ * been taken into account.
+ *
+ * 3.3. DOA RDATA Presentation Format
+ *
+ * The DOA-ENTERPRISE field is presented as an unsigned 32-bit decimal
+ * integer with range 0 - 4,294,967,295.
+ *
+ * The DOA-TYPE field is presented as an unsigned 32-bit decimal integer
+ * with range 0 - 4,294,967,295.
+ *
+ * The DOA-LOCATION field is presented as an unsigned 8-bit decimal
+ * integer with range 0 - 255.
+ *
+ * The DOA-MEDIA-TYPE field is presented as a single <character-string>.
+ *
+ * The DOA-DATA is presented as Base64 encoded data [RFC4648] unless the
+ * DOA-DATA is empty in which case it is presented as a single dash
+ * character ("-", ASCII 45). White space is permitted within Base64
+ * data.
+ */
+ATF_TC(doa);
+ATF_TC_HEAD(doa, tc) {
+ atf_tc_set_md_var(tc, "descr", "DOA RDATA manipulations");
+}
+ATF_TC_BODY(doa, tc) {
+ text_ok_t text_ok[] = {
+ /*
+ * Valid, non-empty DOA-DATA.
+ */
+ TEXT_VALID("0 0 1 \"text/plain\" Zm9v"),
+ /*
+ * Valid, non-empty DOA-DATA with whitespace in between.
+ */
+ TEXT_VALID_CHANGED("0 0 1 \"text/plain\" Zm 9v",
+ "0 0 1 \"text/plain\" Zm9v"),
+ /*
+ * Valid, unquoted DOA-MEDIA-TYPE, non-empty DOA-DATA.
+ */
+ TEXT_VALID_CHANGED("0 0 1 text/plain Zm9v",
+ "0 0 1 \"text/plain\" Zm9v"),
+ /*
+ * Invalid, quoted non-empty DOA-DATA.
+ */
+ TEXT_INVALID("0 0 1 \"text/plain\" \"Zm9v\""),
+ /*
+ * Valid, empty DOA-DATA.
+ */
+ TEXT_VALID("0 0 1 \"text/plain\" -"),
+ /*
+ * Invalid, quoted empty DOA-DATA.
+ */
+ TEXT_INVALID("0 0 1 \"text/plain\" \"-\""),
+ /*
+ * Invalid, missing "-" in empty DOA-DATA.
+ */
+ TEXT_INVALID("0 0 1 \"text/plain\""),
+ /*
+ * Valid, undefined DOA-LOCATION.
+ */
+ TEXT_VALID("0 0 100 \"text/plain\" Zm9v"),
+ /*
+ * Invalid, DOA-LOCATION too big.
+ */
+ TEXT_INVALID("0 0 256 \"text/plain\" ZM9v"),
+ /*
+ * Valid, empty DOA-MEDIA-TYPE, non-empty DOA-DATA.
+ */
+ TEXT_VALID("0 0 2 \"\" aHR0cHM6Ly93d3cuaXNjLm9yZy8="),
+ /*
+ * Valid, empty DOA-MEDIA-TYPE, empty DOA-DATA.
+ */
+ TEXT_VALID("0 0 1 \"\" -"),
+ /*
+ * Valid, DOA-MEDIA-TYPE with a space.
+ */
+ TEXT_VALID("0 0 1 \"plain text\" Zm9v"),
+ /*
+ * Invalid, missing DOA-MEDIA-TYPE.
+ */
+ TEXT_INVALID("1234567890 1234567890 1"),
+ /*
+ * Valid, DOA-DATA over 255 octets.
+ */
+ TEXT_VALID("1234567890 1234567890 1 \"image/gif\" "
+ "R0lGODlhKAAZAOMCAGZmZgBmmf///zOZzMz//5nM/zNmmWbM"
+ "/5nMzMzMzACZ/////////////////////yH5BAEKAA8ALAAA"
+ "AAAoABkAAATH8IFJK5U2a4337F5ogRkpnoCJrly7PrCKyh8c"
+ "3HgAhzT35MDbbtO7/IJIHbGiOiaTxVTpSVWWLqNq1UVyapNS"
+ "1wd3OAxug0LhnCubcVhsxysQnOt4ATpvvzHlFzl1AwODhWeF"
+ "AgRpen5/UhheAYMFdUB4SFcpGEGGdQeCAqBBLTuSk30EeXd9"
+ "pEsAbKGxjHqDSE0Sp6ixN4N1BJmbc7lIhmsBich1awPAjkY1"
+ "SZR8bJWrz382SGqIBQQFQd4IsUTaX+ceuudPEQA7"),
+ /*
+ * Invalid, bad Base64 in DOA-DATA.
+ */
+ TEXT_INVALID("1234567890 1234567890 1 \"image/gif\" R0lGODl"),
+ /*
+ * Sentinel.
+ */
+ TEXT_SENTINEL()
+ };
+ wire_ok_t wire_ok[] = {
+ /*
+ * Valid, empty DOA-MEDIA-TYPE, empty DOA-DATA.
+ */
+ WIRE_VALID(0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78,
+ 0x01, 0x00),
+ /*
+ * Invalid, missing DOA-MEDIA-TYPE.
+ */
+ WIRE_INVALID(0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78,
+ 0x01),
+ /*
+ * Invalid, malformed DOA-MEDIA-TYPE length.
+ */
+ WIRE_INVALID(0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78,
+ 0x01, 0xff),
+ /*
+ * Valid, empty DOA-DATA.
+ */
+ WIRE_VALID(0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78,
+ 0x01, 0x03, 0x66, 0x6f, 0x6f),
+ /*
+ * Valid, non-empty DOA-DATA.
+ */
+ WIRE_VALID(0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78,
+ 0x01, 0x03, 0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72),
+ /*
+ * Valid, DOA-DATA over 255 octets.
+ */
+ WIRE_VALID(0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78,
+ 0x01, 0x06, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79,
+ 0x00, 0x66, 0x99, 0xff, 0xff, 0xff, 0x33, 0x99,
+ 0xcc, 0xcc, 0xff, 0xff, 0x99, 0xcc, 0xff, 0x33,
+ 0x66, 0x99, 0x66, 0xcc, 0xff, 0x99, 0xcc, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0x00, 0x99, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x21, 0xf9, 0x04,
+ 0x01, 0x0a, 0x00, 0x0f, 0x00, 0x2c, 0x00, 0x00,
+ 0x00, 0x00, 0x28, 0x00, 0x19, 0x00, 0x00, 0x04,
+ 0xc7, 0xf0, 0x81, 0x49, 0x2b, 0x95, 0x36, 0x6b,
+ 0x8d, 0xf7, 0xec, 0x5e, 0x68, 0x81, 0x19, 0x29,
+ 0x9e, 0x80, 0x89, 0xae, 0x5c, 0xbb, 0x3e, 0xb0,
+ 0x8a, 0xca, 0x1f, 0x1c, 0xdc, 0x78, 0x00, 0x87,
+ 0x34, 0xf7, 0xe4, 0xc0, 0xdb, 0x6e, 0xd3, 0xbb,
+ 0xfc, 0x82, 0x48, 0x1d, 0xb1, 0xa2, 0x3a, 0x26,
+ 0x93, 0xc5, 0x54, 0xe9, 0x49, 0x55, 0x96, 0x2e,
+ 0xa3, 0x6a, 0xd5, 0x45, 0x72, 0x6a, 0x93, 0x52,
+ 0xd7, 0x07, 0x77, 0x38, 0x0c, 0x6e, 0x83, 0x42,
+ 0xe1, 0x9c, 0x2b, 0x9b, 0x71, 0x58, 0x6c, 0xc7,
+ 0x2b, 0x10, 0x9c, 0xeb, 0x78, 0x01, 0x3a, 0x6f,
+ 0xbf, 0x31, 0xe5, 0x17, 0x39, 0x75, 0x03, 0x03,
+ 0x83, 0x85, 0x67, 0x85, 0x02, 0x04, 0x69, 0x7a,
+ 0x7e, 0x7f, 0x52, 0x18, 0x5e, 0x01, 0x83, 0x05,
+ 0x75, 0x40, 0x78, 0x48, 0x57, 0x29, 0x18, 0x41,
+ 0x86, 0x75, 0x07, 0x82, 0x02, 0xa0, 0x41, 0x2d,
+ 0x3b, 0x92, 0x93, 0x7d, 0x04, 0x79, 0x77, 0x7d,
+ 0xa4, 0x4b, 0x00, 0x6c, 0xa1, 0xb1, 0x8c, 0x7a,
+ 0x83, 0x48, 0x4d, 0x12, 0xa7, 0xa8, 0xb1, 0x37,
+ 0x83, 0x75, 0x04, 0x99, 0x9b, 0x73, 0xb9, 0x48,
+ 0x86, 0x6b, 0x01, 0x89, 0xc8, 0x75, 0x6b, 0x03,
+ 0xc0, 0x8e, 0x46, 0x35, 0x49, 0x94, 0x7c, 0x6c,
+ 0x95, 0xab, 0xcf, 0x7f, 0x36, 0x48, 0x6a, 0x88,
+ 0x05, 0x04, 0x05, 0x41, 0xde, 0x08, 0xb1, 0x44,
+ 0xda, 0x5f, 0xe7, 0x1e, 0xba, 0xe7, 0x4f, 0x11,
+ 0x00, 0x3b),
+ /*
+ * Sentinel.
+ */
+ WIRE_SENTINEL()
+ };
+
+ UNUSED(tc);
+
+ check_rdata(text_ok, wire_ok, false, dns_rdataclass_in,
+ dns_rdatatype_doa, sizeof(dns_rdata_doa_t));
+}
+
+/*
+ * EDNS Client Subnet tests.
+ *
+ * RFC 7871:
+ *
+ * 6. Option Format
+ *
+ * This protocol uses an EDNS0 [RFC6891] option to include client
+ * address information in DNS messages. The option is structured as
+ * follows:
+ *
+ * +0 (MSB) +1 (LSB)
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * 0: | OPTION-CODE |
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * 2: | OPTION-LENGTH |
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * 4: | FAMILY |
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * 6: | SOURCE PREFIX-LENGTH | SCOPE PREFIX-LENGTH |
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * 8: | ADDRESS... /
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ *
+ * o (Defined in [RFC6891]) OPTION-CODE, 2 octets, for ECS is 8 (0x00
+ * 0x08).
+ *
+ * o (Defined in [RFC6891]) OPTION-LENGTH, 2 octets, contains the
+ * length of the payload (everything after OPTION-LENGTH) in octets.
+ *
+ * o FAMILY, 2 octets, indicates the family of the address contained in
+ * the option, using address family codes as assigned by IANA in
+ * Address Family Numbers [Address_Family_Numbers].
+ *
+ * The format of the address part depends on the value of FAMILY. This
+ * document only defines the format for FAMILY 1 (IPv4) and FAMILY 2
+ * (IPv6), which are as follows:
+ *
+ * o SOURCE PREFIX-LENGTH, an unsigned octet representing the leftmost
+ * number of significant bits of ADDRESS to be used for the lookup.
+ * In responses, it mirrors the same value as in the queries.
+ *
+ * o SCOPE PREFIX-LENGTH, an unsigned octet representing the leftmost
+ * number of significant bits of ADDRESS that the response covers.
+ * In queries, it MUST be set to 0.
+ *
+ * o ADDRESS, variable number of octets, contains either an IPv4 or
+ * IPv6 address, depending on FAMILY, which MUST be truncated to the
+ * number of bits indicated by the SOURCE PREFIX-LENGTH field,
+ * padding with 0 bits to pad to the end of the last octet needed.
+ *
+ * o A server receiving an ECS option that uses either too few or too
+ * many ADDRESS octets, or that has non-zero ADDRESS bits set beyond
+ * SOURCE PREFIX-LENGTH, SHOULD return FORMERR to reject the packet,
+ * as a signal to the software developer making the request to fix
+ * their implementation.
+ *
+ * All fields are in network byte order ("big-endian", per [RFC1700],
+ * Data Notation).
+ */
+ATF_TC(edns_client_subnet);
+ATF_TC_HEAD(edns_client_subnet, tc) {
+ atf_tc_set_md_var(tc, "descr",
+ "OPT RDATA with EDNS Client Subnet manipulations");
+}
+ATF_TC_BODY(edns_client_subnet, tc) {
+ wire_ok_t wire_ok[] = {
+ /*
+ * Option code with no content.
+ */
+ WIRE_INVALID(0x00, 0x08, 0x00, 0x00),
+ /*
+ * Option code family 0, source 0, scope 0.
+ */
+ WIRE_VALID(0x00, 0x08, 0x00, 0x04,
+ 0x00, 0x00, 0x00, 0x00),
+ /*
+ * Option code family 1 (IPv4), source 0, scope 0.
+ */
+ WIRE_VALID(0x00, 0x08, 0x00, 0x04,
+ 0x00, 0x01, 0x00, 0x00),
+ /*
+ * Option code family 2 (IPv6) , source 0, scope 0.
+ */
+ WIRE_VALID(0x00, 0x08, 0x00, 0x04,
+ 0x00, 0x02, 0x00, 0x00),
+ /*
+ * Extra octet.
+ */
+ WIRE_INVALID(0x00, 0x08, 0x00, 0x05,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00),
+ /*
+ * Source too long for IPv4.
+ */
+ WIRE_INVALID(0x00, 0x08, 0x00, 8,
+ 0x00, 0x01, 33, 0x00,
+ 0x00, 0x00, 0x00, 0x00),
+ /*
+ * Source too long for IPv6.
+ */
+ WIRE_INVALID(0x00, 0x08, 0x00, 20,
+ 0x00, 0x02, 129, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00),
+ /*
+ * Scope too long for IPv4.
+ */
+ WIRE_INVALID(0x00, 0x08, 0x00, 8,
+ 0x00, 0x01, 0x00, 33,
+ 0x00, 0x00, 0x00, 0x00),
+ /*
+ * Scope too long for IPv6.
+ */
+ WIRE_INVALID(0x00, 0x08, 0x00, 20,
+ 0x00, 0x02, 0x00, 129,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00),
+ /*
+ * When family=0, source and scope should be 0.
+ */
+ WIRE_VALID(0x00, 0x08, 0x00, 4,
+ 0x00, 0x00, 0x00, 0x00),
+ /*
+ * When family=0, source and scope should be 0.
+ */
+ WIRE_INVALID(0x00, 0x08, 0x00, 5,
+ 0x00, 0x00, 0x01, 0x00,
+ 0x00),
+ /*
+ * When family=0, source and scope should be 0.
+ */
+ WIRE_INVALID(0x00, 0x08, 0x00, 5,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00),
+ /*
+ * Length too short for source IPv4.
+ */
+ WIRE_INVALID(0x00, 0x08, 0x00, 7,
+ 0x00, 0x01, 32, 0x00,
+ 0x00, 0x00, 0x00),
+ /*
+ * Length too short for source IPv6.
+ */
+ WIRE_INVALID(0x00, 0x08, 0x00, 19,
+ 0x00, 0x02, 128, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00),
+ /*
+ * Sentinel.
+ */
+ WIRE_SENTINEL()
+ };
+
+ UNUSED(tc);
+
+ check_rdata(NULL, wire_ok, true, dns_rdataclass_in,
+ dns_rdatatype_opt, sizeof(dns_rdata_opt_t));
+}
+
+/*
+ * Successful load test.
+ */
+ATF_TC(hip);
+ATF_TC_HEAD(hip, tc) {
+ atf_tc_set_md_var(tc, "descr", "that a oversized HIP record will "
+ "be rejected");
+}
+ATF_TC_BODY(hip, tc) {
+ unsigned char hipwire[DNS_RDATA_MAXLENGTH] = {
+ 0x01, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x04, 0x41, 0x42, 0x43, 0x44, 0x00 };
+ unsigned char buf[1024*1024];
+ isc_buffer_t source, target;
+ dns_rdata_t rdata;
+ dns_decompress_t dctx;
+ isc_result_t result;
+ size_t i;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ /*
+ * Fill the rest of input buffer with compression pointers.
+ */
+ for (i = 12; i < sizeof(hipwire) - 2; i += 2) {
+ hipwire[i] = 0xc0;
+ hipwire[i+1] = 0x06;
+ }
+
+ isc_buffer_init(&source, hipwire, sizeof(hipwire));
+ isc_buffer_add(&source, sizeof(hipwire));
+ isc_buffer_setactive(&source, i);
+ isc_buffer_init(&target, buf, sizeof(buf));
+ dns_rdata_init(&rdata);
+ dns_decompress_init(&dctx, -1, DNS_DECOMPRESS_ANY);
+ result = dns_rdata_fromwire(&rdata, dns_rdataclass_in,
+ dns_rdatatype_hip, &source, &dctx,
+ 0, &target);
+ dns_decompress_invalidate(&dctx);
+ ATF_REQUIRE_EQ(result, DNS_R_FORMERR);
+
+ dns_test_end();
+}
+
+/*
+ * ISDN tests.
+ *
+ * RFC 1183:
+ *
+ * 3.2. The ISDN RR
+ *
+ * The ISDN RR is defined with mnemonic ISDN and type code 20 (decimal).
+ *
+ * An ISDN (Integrated Service Digital Network) number is simply a
+ * telephone number. The intent of the members of the CCITT is to
+ * upgrade all telephone and data network service to a common service.
+ *
+ * The numbering plan (E.163/E.164) is the same as the familiar
+ * international plan for POTS (an un-official acronym, meaning Plain
+ * Old Telephone Service). In E.166, CCITT says "An E.163/E.164
+ * telephony subscriber may become an ISDN subscriber without a number
+ * change."
+ *
+ * ISDN has the following format:
+ *
+ * <owner> <ttl> <class> ISDN <ISDN-address> <sa>
+ *
+ * The <ISDN-address> field is required; <sa> is optional.
+ *
+ * <ISDN-address> identifies the ISDN number of <owner> and DDI (Direct
+ * Dial In) if any, as defined by E.164 [8] and E.163 [7], the ISDN and
+ * PSTN (Public Switched Telephone Network) numbering plan. E.163
+ * defines the country codes, and E.164 the form of the addresses. Its
+ * format in master files is a <character-string> syntactically
+ * identical to that used in TXT and HINFO.
+ *
+ * <sa> specifies the subaddress (SA). The format of <sa> in master
+ * files is a <character-string> syntactically identical to that used in
+ * TXT and HINFO.
+ *
+ * The format of ISDN is class insensitive. ISDN RRs cause no
+ * additional section processing.
+ *
+ * The <ISDN-address> is a string of characters, normally decimal
+ * digits, beginning with the E.163 country code and ending with the DDI
+ * if any. Note that ISDN, in Q.931, permits any IA5 character in the
+ * general case.
+ *
+ * The <sa> is a string of hexadecimal digits. For digits 0-9, the
+ * concrete encoding in the Q.931 call setup information element is
+ * identical to BCD.
+ *
+ * For example:
+ *
+ * Relay.Prime.COM. IN ISDN 150862028003217
+ * sh.Prime.COM. IN ISDN 150862028003217 004
+ *
+ * (Note: "1" is the country code for the North American Integrated
+ * Numbering Area, i.e., the system of "area codes" familiar to people
+ * in those countries.)
+ *
+ * The RR data is the ASCII representation of the digits. It is encoded
+ * as one or two <character-string>s, i.e., count followed by
+ * characters.
+ */
+ATF_TC(isdn);
+ATF_TC_HEAD(isdn, tc) {
+ atf_tc_set_md_var(tc, "descr", "ISDN RDATA manipulations");
+}
+ATF_TC_BODY(isdn, tc) {
+ wire_ok_t wire_ok[] = {
+ /*
+ * "".
+ */
+ WIRE_VALID(0x00),
+ /*
+ * "\001".
+ */
+ WIRE_VALID(0x01, 0x01),
+ /*
+ * "\001" "".
+ */
+ WIRE_VALID(0x01, 0x01, 0x00),
+ /*
+ * "\001" "\001".
+ */
+ WIRE_VALID(0x01, 0x01, 0x01, 0x01),
+ /*
+ * Sentinel.
+ */
+ WIRE_SENTINEL()
+ };
+
+ UNUSED(tc);
+
+ check_rdata(NULL, wire_ok, false, dns_rdataclass_in,
+ dns_rdatatype_isdn, sizeof(dns_rdata_isdn_t));
+}
+
+/*
+ * NSEC tests.
+ *
+ * RFC 4034:
+ *
+ * 4.1. NSEC RDATA Wire Format
+ *
+ * The RDATA of the NSEC RR is as shown below:
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * / Next Domain Name /
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * / Type Bit Maps /
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * 4.1.1. The Next Domain Name Field
+ *
+ * The Next Domain field contains the next owner name (in the canonical
+ * ordering of the zone) that has authoritative data or contains a
+ * delegation point NS RRset; see Section 6.1 for an explanation of
+ * canonical ordering. The value of the Next Domain Name field in the
+ * last NSEC record in the zone is the name of the zone apex (the owner
+ * name of the zone's SOA RR). This indicates that the owner name of
+ * the NSEC RR is the last name in the canonical ordering of the zone.
+ *
+ * A sender MUST NOT use DNS name compression on the Next Domain Name
+ * field when transmitting an NSEC RR.
+ *
+ * Owner names of RRsets for which the given zone is not authoritative
+ * (such as glue records) MUST NOT be listed in the Next Domain Name
+ * unless at least one authoritative RRset exists at the same owner
+ * name.
+ *
+ * 4.1.2. The Type Bit Maps Field
+ *
+ * The Type Bit Maps field identifies the RRset types that exist at the
+ * NSEC RR's owner name.
+ *
+ * The RR type space is split into 256 window blocks, each representing
+ * the low-order 8 bits of the 16-bit RR type space. Each block that
+ * has at least one active RR type is encoded using a single octet
+ * window number (from 0 to 255), a single octet bitmap length (from 1
+ * to 32) indicating the number of octets used for the window block's
+ * bitmap, and up to 32 octets (256 bits) of bitmap.
+ *
+ * Blocks are present in the NSEC RR RDATA in increasing numerical
+ * order.
+ *
+ * Type Bit Maps Field = ( Window Block # | Bitmap Length | Bitmap )+
+ *
+ * where "|" denotes concatenation.
+ *
+ * Each bitmap encodes the low-order 8 bits of RR types within the
+ * window block, in network bit order. The first bit is bit 0. For
+ * window block 0, bit 1 corresponds to RR type 1 (A), bit 2 corresponds
+ * to RR type 2 (NS), and so forth. For window block 1, bit 1
+ * corresponds to RR type 257, and bit 2 to RR type 258. If a bit is
+ * set, it indicates that an RRset of that type is present for the NSEC
+ * RR's owner name. If a bit is clear, it indicates that no RRset of
+ * that type is present for the NSEC RR's owner name.
+ *
+ * Bits representing pseudo-types MUST be clear, as they do not appear
+ * in zone data. If encountered, they MUST be ignored upon being read.
+ */
+ATF_TC(nsec);
+ATF_TC_HEAD(nsec, tc) {
+ atf_tc_set_md_var(tc, "descr", "NSEC RDATA manipulations");
+}
+ATF_TC_BODY(nsec, tc) {
+ text_ok_t text_ok[] = {
+ TEXT_INVALID(""),
+ TEXT_INVALID("."),
+ TEXT_VALID(". RRSIG"),
+ TEXT_SENTINEL()
+ };
+ wire_ok_t wire_ok[] = {
+ WIRE_INVALID(0x00),
+ WIRE_INVALID(0x00, 0x00),
+ WIRE_INVALID(0x00, 0x00, 0x00),
+ WIRE_VALID(0x00, 0x00, 0x01, 0x02),
+ WIRE_INVALID()
+ };
+
+ UNUSED(tc);
+
+ check_rdata(text_ok, wire_ok, false, dns_rdataclass_in,
+ dns_rdatatype_nsec, sizeof(dns_rdata_nsec_t));
+}
+
+/*
+ * NSEC3 tests.
+ *
+ * RFC 5155.
+ */
+ATF_TC(nsec3);
+ATF_TC_HEAD(nsec3, tc) {
+ atf_tc_set_md_var(tc, "descr", "NSEC3 RDATA manipulations");
+}
+ATF_TC_BODY(nsec3, tc) {
+ text_ok_t text_ok[] = {
+ TEXT_INVALID(""),
+ TEXT_INVALID("."),
+ TEXT_INVALID(". RRSIG"),
+ TEXT_INVALID("1 0 10 76931F"),
+ TEXT_INVALID("1 0 10 76931F IMQ912BREQP1POLAH3RMONG;UED541AS"),
+ TEXT_INVALID("1 0 10 76931F IMQ912BREQP1POLAH3RMONG;UED541AS A RRSIG"),
+ TEXT_VALID("1 0 10 76931F AJHVGTICN6K0VDA53GCHFMT219SRRQLM A RRSIG"),
+ TEXT_VALID("1 0 10 76931F AJHVGTICN6K0VDA53GCHFMT219SRRQLM"),
+ TEXT_VALID("1 0 10 - AJHVGTICN6K0VDA53GCHFMT219SRRQLM"),
+ TEXT_SENTINEL()
+ };
+
+ UNUSED(tc);
+
+ check_rdata(text_ok, NULL, false, dns_rdataclass_in,
+ dns_rdatatype_nsec3, sizeof(dns_rdata_nsec3_t));
+}
+
+/*
+ * WKS tests.
+ *
+ * RFC 1035:
+ *
+ * 3.4.2. WKS RDATA format
+ *
+ * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ * | ADDRESS |
+ * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ * | PROTOCOL | |
+ * +--+--+--+--+--+--+--+--+ |
+ * | |
+ * / <BIT MAP> /
+ * / /
+ * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *
+ * where:
+ *
+ * ADDRESS An 32 bit Internet address
+ *
+ * PROTOCOL An 8 bit IP protocol number
+ *
+ * <BIT MAP> A variable length bit map. The bit map must be a
+ * multiple of 8 bits long.
+ *
+ * The WKS record is used to describe the well known services supported by
+ * a particular protocol on a particular internet address. The PROTOCOL
+ * field specifies an IP protocol number, and the bit map has one bit per
+ * port of the specified protocol. The first bit corresponds to port 0,
+ * the second to port 1, etc. If the bit map does not include a bit for a
+ * protocol of interest, that bit is assumed zero. The appropriate values
+ * and mnemonics for ports and protocols are specified in [RFC-1010].
+ *
+ * For example, if PROTOCOL=TCP (6), the 26th bit corresponds to TCP port
+ * 25 (SMTP). If this bit is set, a SMTP server should be listening on TCP
+ * port 25; if zero, SMTP service is not supported on the specified
+ * address.
+ */
+ATF_TC(wks);
+ATF_TC_HEAD(wks, tc) {
+ atf_tc_set_md_var(tc, "descr", "WKS RDATA manipulations");
+}
+ATF_TC_BODY(wks, tc) {
+ wire_ok_t wire_ok[] = {
+ /*
+ * Too short.
+ */
+ WIRE_INVALID(0x00, 0x08, 0x00, 0x00),
+ /*
+ * Minimal TCP.
+ */
+ WIRE_VALID(0x00, 0x08, 0x00, 0x00, 6),
+ /*
+ * Minimal UDP.
+ */
+ WIRE_VALID(0x00, 0x08, 0x00, 0x00, 17),
+ /*
+ * Minimal other.
+ */
+ WIRE_VALID(0x00, 0x08, 0x00, 0x00, 1),
+ /*
+ * Sentinel.
+ */
+ WIRE_SENTINEL()
+ };
+
+ UNUSED(tc);
+
+ check_rdata(NULL, wire_ok, false, dns_rdataclass_in,
+ dns_rdatatype_wks, sizeof(dns_rdata_in_wks_t));
+}
+
+/*****
+ ***** Main
+ *****/
+
+ATF_TP_ADD_TCS(tp) {
+ ATF_TP_ADD_TC(tp, csync);
+ ATF_TP_ADD_TC(tp, doa);
+ ATF_TP_ADD_TC(tp, edns_client_subnet);
+ ATF_TP_ADD_TC(tp, hip);
+ ATF_TP_ADD_TC(tp, isdn);
+ ATF_TP_ADD_TC(tp, nsec);
+ ATF_TP_ADD_TC(tp, nsec3);
+ ATF_TP_ADD_TC(tp, wks);
+
+ return (atf_no_error());
+}
diff --git a/lib/dns/tests/rdataset_test.c b/lib/dns/tests/rdataset_test.c
new file mode 100644
index 0000000..b055846
--- /dev/null
+++ b/lib/dns/tests/rdataset_test.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <atf-c.h>
+
+#include <unistd.h>
+
+#include <dns/rdataset.h>
+#include <dns/rdatastruct.h>
+
+#include "dnstest.h"
+
+
+/*
+ * Individual unit tests
+ */
+
+/* Successful load test */
+ATF_TC(trimttl);
+ATF_TC_HEAD(trimttl, tc) {
+ atf_tc_set_md_var(tc, "descr", "dns_master_loadfile() loads a "
+ "valid master file and returns success");
+}
+ATF_TC_BODY(trimttl, tc) {
+ isc_result_t result;
+ dns_rdataset_t rdataset, sigrdataset;
+ dns_rdata_rrsig_t rrsig;
+ isc_stdtime_t ttltimenow, ttltimeexpire;
+
+ ttltimenow = 10000000;
+ ttltimeexpire = ttltimenow + 800;
+
+ UNUSED(tc);
+
+ dns_rdataset_init(&rdataset);
+ dns_rdataset_init(&sigrdataset);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ rdataset.ttl = 900;
+ sigrdataset.ttl = 1000;
+ rrsig.timeexpire = ttltimeexpire;
+ rrsig.originalttl = 1000;
+
+ dns_rdataset_trimttl(&rdataset, &sigrdataset, &rrsig, ttltimenow,
+ true);
+ ATF_REQUIRE_EQ(rdataset.ttl, 800);
+ ATF_REQUIRE_EQ(sigrdataset.ttl, 800);
+
+ rdataset.ttl = 900;
+ sigrdataset.ttl = 1000;
+ rrsig.timeexpire = ttltimenow - 200;
+ rrsig.originalttl = 1000;
+
+ dns_rdataset_trimttl(&rdataset, &sigrdataset, &rrsig, ttltimenow,
+ true);
+ ATF_REQUIRE_EQ(rdataset.ttl, 120);
+ ATF_REQUIRE_EQ(sigrdataset.ttl, 120);
+
+ rdataset.ttl = 900;
+ sigrdataset.ttl = 1000;
+ rrsig.timeexpire = ttltimenow - 200;
+ rrsig.originalttl = 1000;
+
+ dns_rdataset_trimttl(&rdataset, &sigrdataset, &rrsig, ttltimenow,
+ false);
+ ATF_REQUIRE_EQ(rdataset.ttl, 0);
+ ATF_REQUIRE_EQ(sigrdataset.ttl, 0);
+
+ sigrdataset.ttl = 900;
+ rdataset.ttl = 1000;
+ rrsig.timeexpire = ttltimeexpire;
+ rrsig.originalttl = 1000;
+
+ dns_rdataset_trimttl(&rdataset, &sigrdataset, &rrsig, ttltimenow,
+ true);
+ ATF_REQUIRE_EQ(rdataset.ttl, 800);
+ ATF_REQUIRE_EQ(sigrdataset.ttl, 800);
+
+ sigrdataset.ttl = 900;
+ rdataset.ttl = 1000;
+ rrsig.timeexpire = ttltimenow - 200;
+ rrsig.originalttl = 1000;
+
+ dns_rdataset_trimttl(&rdataset, &sigrdataset, &rrsig, ttltimenow,
+ true);
+ ATF_REQUIRE_EQ(rdataset.ttl, 120);
+ ATF_REQUIRE_EQ(sigrdataset.ttl, 120);
+
+ sigrdataset.ttl = 900;
+ rdataset.ttl = 1000;
+ rrsig.timeexpire = ttltimenow - 200;
+ rrsig.originalttl = 1000;
+
+ dns_rdataset_trimttl(&rdataset, &sigrdataset, &rrsig, ttltimenow,
+ false);
+ ATF_REQUIRE_EQ(rdataset.ttl, 0);
+ ATF_REQUIRE_EQ(sigrdataset.ttl, 0);
+
+ dns_test_end();
+}
+
+/*
+ * Main
+ */
+ATF_TP_ADD_TCS(tp) {
+ ATF_TP_ADD_TC(tp, trimttl);
+
+ return (atf_no_error());
+}
+
diff --git a/lib/dns/tests/rdatasetstats_test.c b/lib/dns/tests/rdatasetstats_test.c
new file mode 100644
index 0000000..0c2d5c5
--- /dev/null
+++ b/lib/dns/tests/rdatasetstats_test.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <atf-c.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <unistd.h>
+
+#include <isc/print.h>
+
+#include <dns/stats.h>
+
+#include "dnstest.h"
+
+/*
+ * Helper functions
+ */
+static void
+set_typestats(dns_stats_t *stats, dns_rdatatype_t type,
+ bool stale)
+{
+ dns_rdatastatstype_t which;
+ unsigned int attributes;
+
+ attributes = 0;
+ if (stale) attributes |= DNS_RDATASTATSTYPE_ATTR_STALE;
+ which = DNS_RDATASTATSTYPE_VALUE(type, attributes);
+ dns_rdatasetstats_increment(stats, which);
+
+ attributes = DNS_RDATASTATSTYPE_ATTR_NXRRSET;
+ if (stale) attributes |= DNS_RDATASTATSTYPE_ATTR_STALE;
+ which = DNS_RDATASTATSTYPE_VALUE(type, attributes);
+ dns_rdatasetstats_increment(stats, which);
+}
+
+static void
+set_nxdomainstats(dns_stats_t *stats, bool stale) {
+ dns_rdatastatstype_t which;
+ unsigned int attributes;
+
+ attributes = DNS_RDATASTATSTYPE_ATTR_NXDOMAIN;
+ if (stale) attributes |= DNS_RDATASTATSTYPE_ATTR_STALE;
+ which = DNS_RDATASTATSTYPE_VALUE(0, attributes);
+ dns_rdatasetstats_increment(stats, which);
+}
+
+#define ATTRIBUTE_SET(y) ((attributes & (y)) != 0)
+static void
+checkit1(dns_rdatastatstype_t which, uint64_t value, void *arg) {
+ unsigned int attributes;
+#if debug
+ unsigned int type;
+#endif
+
+ UNUSED(which);
+ UNUSED(arg);
+
+ attributes = DNS_RDATASTATSTYPE_ATTR(which);
+#if debug
+ type = DNS_RDATASTATSTYPE_BASE(which);
+
+ fprintf(stderr, "%s%s%s%s/%u, %u\n",
+ ATTRIBUTE_SET(DNS_RDATASTATSTYPE_ATTR_OTHERTYPE) ? "O" : " ",
+ ATTRIBUTE_SET(DNS_RDATASTATSTYPE_ATTR_NXRRSET) ? "!" : " ",
+ ATTRIBUTE_SET(DNS_RDATASTATSTYPE_ATTR_STALE) ? "#" : " ",
+ ATTRIBUTE_SET(DNS_RDATASTATSTYPE_ATTR_NXDOMAIN) ? "X" : " ",
+ type, (unsigned)value);
+#endif
+ if ((attributes & DNS_RDATASTATSTYPE_ATTR_STALE) == 0)
+ ATF_REQUIRE_EQ(value, 1);
+ else
+ ATF_REQUIRE_EQ(value, 0);
+}
+
+static void
+checkit2(dns_rdatastatstype_t which, uint64_t value, void *arg) {
+ unsigned int attributes;
+#if debug
+ unsigned int type;
+#endif
+
+ UNUSED(which);
+ UNUSED(arg);
+
+ attributes = DNS_RDATASTATSTYPE_ATTR(which);
+#if debug
+ type = DNS_RDATASTATSTYPE_BASE(which);
+
+ fprintf(stderr, "%s%s%s%s/%u, %u\n",
+ ATTRIBUTE_SET(DNS_RDATASTATSTYPE_ATTR_OTHERTYPE) ? "O" : " ",
+ ATTRIBUTE_SET(DNS_RDATASTATSTYPE_ATTR_NXRRSET) ? "!" : " ",
+ ATTRIBUTE_SET(DNS_RDATASTATSTYPE_ATTR_STALE) ? "#" : " ",
+ ATTRIBUTE_SET(DNS_RDATASTATSTYPE_ATTR_NXDOMAIN) ? "X" : " ",
+ type, (unsigned)value);
+#endif
+ if ((attributes & DNS_RDATASTATSTYPE_ATTR_STALE) == 0)
+ ATF_REQUIRE_EQ(value, 0);
+ else
+ ATF_REQUIRE_EQ(value, 1);
+}
+/*
+ * Individual unit tests
+ */
+
+ATF_TC(rdatasetstats);
+ATF_TC_HEAD(rdatasetstats, tc) {
+ atf_tc_set_md_var(tc, "descr", "test that rdatasetstats counters are properly set");
+}
+ATF_TC_BODY(rdatasetstats, tc) {
+ unsigned int i;
+ dns_stats_t *stats = NULL;
+ isc_result_t result;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, true);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_rdatasetstats_create(mctx, &stats);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ /* First 256 types. */
+ for (i = 0; i <= 255; i++)
+ set_typestats(stats, (dns_rdatatype_t)i, false);
+ /* Specials */
+ set_typestats(stats, dns_rdatatype_dlv, false);
+ set_typestats(stats, (dns_rdatatype_t)1000, false);
+ set_nxdomainstats(stats, false);
+
+ /*
+ * Check that all counters are set to appropriately.
+ */
+ dns_rdatasetstats_dump(stats, checkit1, NULL, 1);
+
+ /* First 256 types. */
+ for (i = 0; i <= 255; i++)
+ set_typestats(stats, (dns_rdatatype_t)i, true);
+ /* Specials */
+ set_typestats(stats, dns_rdatatype_dlv, true);
+ set_typestats(stats, (dns_rdatatype_t)1000, true);
+ set_nxdomainstats(stats, true);
+
+ /*
+ * Check that all counters are set to appropriately.
+ */
+ dns_rdatasetstats_dump(stats, checkit2, NULL, 1);
+
+ dns_stats_detach(&stats);
+ dns_test_end();
+}
+
+/*
+ * Main
+ */
+ATF_TP_ADD_TCS(tp) {
+ ATF_TP_ADD_TC(tp, rdatasetstats);
+ return (atf_no_error());
+}
diff --git a/lib/dns/tests/resolver_test.c b/lib/dns/tests/resolver_test.c
new file mode 100644
index 0000000..518622d
--- /dev/null
+++ b/lib/dns/tests/resolver_test.c
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <atf-c.h>
+
+#include <unistd.h>
+
+#include <isc/app.h>
+#include <isc/buffer.h>
+#include <isc/socket.h>
+#include <isc/task.h>
+#include <isc/timer.h>
+
+#include <dns/dispatch.h>
+#include <dns/name.h>
+#include <dns/resolver.h>
+#include <dns/view.h>
+
+#include "dnstest.h"
+
+static dns_dispatchmgr_t *dispatchmgr = NULL;
+static dns_dispatch_t *dispatch = NULL;
+static dns_view_t *view = NULL;
+
+
+static void
+setup(void) {
+ isc_result_t result;
+ isc_sockaddr_t local;
+
+ result = dns_test_begin(NULL, true);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_dispatchmgr_create(mctx, NULL, &dispatchmgr);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_test_makeview("view", &view);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ isc_sockaddr_any(&local);
+ result = dns_dispatch_getudp(dispatchmgr, socketmgr, taskmgr, &local,
+ 4096, 100, 100, 100, 500, 0, 0,
+ &dispatch);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+}
+
+static void
+teardown(void) {
+ dns_dispatch_detach(&dispatch);
+ dns_view_detach(&view);
+ dns_dispatchmgr_destroy(&dispatchmgr);
+ dns_test_end();
+}
+
+
+static void
+mkres(dns_resolver_t **resolverp) {
+ isc_result_t result;
+
+ result = dns_resolver_create(view, taskmgr, 1, 1,
+ socketmgr, timermgr, 0,
+ dispatchmgr, dispatch, NULL, resolverp);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+}
+
+static void
+destroy_resolver(dns_resolver_t **resolverp) {
+ dns_resolver_shutdown(*resolverp);
+ dns_resolver_detach(resolverp);
+}
+
+ATF_TC(create);
+ATF_TC_HEAD(create, tc) {
+ atf_tc_set_md_var(tc, "descr", "dns_resolver_create");
+}
+ATF_TC_BODY(create, tc) {
+ dns_resolver_t *resolver = NULL;
+
+ UNUSED(tc);
+
+ setup();
+ mkres(&resolver);
+ destroy_resolver(&resolver);
+ teardown();
+}
+
+ATF_TC(gettimeout);
+ATF_TC_HEAD(gettimeout, tc) {
+ atf_tc_set_md_var(tc, "descr", "dns_resolver_gettimeout");
+}
+ATF_TC_BODY(gettimeout, tc) {
+ dns_resolver_t *resolver = NULL;
+ unsigned int timeout;
+
+ UNUSED(tc);
+
+ setup();
+ mkres(&resolver);
+
+ timeout = dns_resolver_gettimeout(resolver);
+ ATF_CHECK(timeout > 0);
+
+ destroy_resolver(&resolver);
+ teardown();
+}
+
+ATF_TC(settimeout);
+ATF_TC_HEAD(settimeout, tc) {
+ atf_tc_set_md_var(tc, "descr", "dns_resolver_settimeout");
+}
+ATF_TC_BODY(settimeout, tc) {
+ dns_resolver_t *resolver = NULL;
+ unsigned int default_timeout, timeout;
+
+ UNUSED(tc);
+
+ setup();
+
+ mkres(&resolver);
+
+ default_timeout = dns_resolver_gettimeout(resolver);
+ dns_resolver_settimeout(resolver, default_timeout + 1);
+ timeout = dns_resolver_gettimeout(resolver);
+ ATF_CHECK(timeout == default_timeout + 1);
+
+ destroy_resolver(&resolver);
+ teardown();
+}
+
+ATF_TC(settimeout_default);
+ATF_TC_HEAD(settimeout_default, tc) {
+ atf_tc_set_md_var(tc, "descr", "dns_resolver_settimeout to default");
+}
+ATF_TC_BODY(settimeout_default, tc) {
+ dns_resolver_t *resolver = NULL;
+ unsigned int default_timeout, timeout;
+
+ UNUSED(tc);
+
+ setup();
+
+ mkres(&resolver);
+
+ default_timeout = dns_resolver_gettimeout(resolver);
+ dns_resolver_settimeout(resolver, default_timeout + 10);
+
+ timeout = dns_resolver_gettimeout(resolver);
+ ATF_CHECK_EQ(timeout, default_timeout + 10);
+
+ dns_resolver_settimeout(resolver, 0);
+ timeout = dns_resolver_gettimeout(resolver);
+ ATF_CHECK_EQ(timeout, default_timeout);
+
+ destroy_resolver(&resolver);
+ teardown();
+}
+
+ATF_TC(settimeout_belowmin);
+ATF_TC_HEAD(settimeout_belowmin, tc) {
+ atf_tc_set_md_var(tc, "descr",
+ "dns_resolver_settimeout below minimum");
+}
+ATF_TC_BODY(settimeout_belowmin, tc) {
+ dns_resolver_t *resolver = NULL;
+ unsigned int default_timeout, timeout;
+
+ UNUSED(tc);
+
+ setup();
+
+ mkres(&resolver);
+
+ default_timeout = dns_resolver_gettimeout(resolver);
+ dns_resolver_settimeout(resolver, 9);
+
+ timeout = dns_resolver_gettimeout(resolver);
+ ATF_CHECK_EQ(timeout, default_timeout);
+
+ destroy_resolver(&resolver);
+ teardown();
+}
+
+ATF_TC(settimeout_overmax);
+ATF_TC_HEAD(settimeout_overmax, tc) {
+ atf_tc_set_md_var(tc, "descr", "dns_resolver_settimeout over maximum");
+}
+ATF_TC_BODY(settimeout_overmax, tc) {
+ dns_resolver_t *resolver = NULL;
+ unsigned int timeout;
+
+ UNUSED(tc);
+
+ setup();
+
+ mkres(&resolver);
+
+ dns_resolver_settimeout(resolver, 4000000);
+ timeout = dns_resolver_gettimeout(resolver);
+ ATF_CHECK(timeout < 4000000 && timeout > 0);
+
+ destroy_resolver(&resolver);
+ teardown();
+}
+
+/*
+ * Main
+ */
+ATF_TP_ADD_TCS(tp) {
+ ATF_TP_ADD_TC(tp, create);
+ ATF_TP_ADD_TC(tp, gettimeout);
+ ATF_TP_ADD_TC(tp, settimeout);
+ ATF_TP_ADD_TC(tp, settimeout_default);
+ ATF_TP_ADD_TC(tp, settimeout_belowmin);
+ ATF_TP_ADD_TC(tp, settimeout_overmax);
+ return (atf_no_error());
+}
diff --git a/lib/dns/tests/rsa_test.c b/lib/dns/tests/rsa_test.c
new file mode 100644
index 0000000..fb207ef
--- /dev/null
+++ b/lib/dns/tests/rsa_test.c
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/* ! \file */
+
+#include <config.h>
+
+#include <atf-c.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include <isc/util.h>
+#include <isc/print.h>
+
+#include <pk11/site.h>
+
+#include "dnstest.h"
+
+#include "../dst_internal.h"
+
+#if defined(OPENSSL) || defined(PKCS11CRYPTO)
+
+static unsigned char d[10] = {
+ 0xa, 0x10, 0xbb, 0, 0xfe, 0x15, 0x1, 0x88, 0xcc, 0x7d
+};
+
+static unsigned char sigsha1[256] = {
+ 0x45, 0x55, 0xd6, 0xf8, 0x05, 0xd2, 0x2e, 0x79,
+ 0x14, 0x2b, 0x1b, 0xd1, 0x4b, 0xb7, 0xcd, 0xc0,
+ 0xa2, 0xf3, 0x85, 0x32, 0x1f, 0xa3, 0xfd, 0x1f,
+ 0x30, 0xe0, 0xde, 0xb2, 0x6f, 0x3c, 0x8e, 0x2b,
+ 0x82, 0x92, 0xcd, 0x1c, 0x1b, 0xdf, 0xe6, 0xd5,
+ 0x4d, 0x93, 0xe6, 0xaa, 0x40, 0x28, 0x1b, 0x7b,
+ 0x2e, 0x40, 0x4d, 0xb5, 0x4d, 0x43, 0xe8, 0xfc,
+ 0x93, 0x86, 0x68, 0xe3, 0xbf, 0x73, 0x9a, 0x1e,
+ 0x6b, 0x5d, 0x52, 0xb8, 0x98, 0x1c, 0x94, 0xe1,
+ 0x85, 0x8b, 0xee, 0xb1, 0x4f, 0x22, 0x71, 0xcb,
+ 0xfd, 0xb2, 0xa8, 0x88, 0x64, 0xb4, 0xb1, 0x4a,
+ 0xa1, 0x7a, 0xce, 0x52, 0x83, 0xd8, 0xf2, 0x9e,
+ 0x67, 0x4c, 0xc3, 0x37, 0x74, 0xfe, 0xe0, 0x25,
+ 0x2a, 0xfd, 0xa3, 0x09, 0xff, 0x8a, 0x92, 0x0d,
+ 0xa9, 0xb3, 0x90, 0x23, 0xbe, 0x6a, 0x2c, 0x9e,
+ 0x5c, 0x6d, 0xb4, 0xa7, 0xd7, 0x97, 0xdd, 0xc6,
+ 0xb8, 0xae, 0xd4, 0x88, 0x64, 0x63, 0x1e, 0x85,
+ 0x20, 0x09, 0xea, 0xc4, 0x0b, 0xca, 0xbf, 0x83,
+ 0x5c, 0x89, 0xae, 0x64, 0x15, 0x76, 0x06, 0x51,
+ 0xb6, 0xa1, 0x99, 0xb2, 0x3c, 0x50, 0x99, 0x86,
+ 0x7d, 0xc7, 0xca, 0x4e, 0x1d, 0x2c, 0x17, 0xbb,
+ 0x6c, 0x7a, 0xc9, 0x3f, 0x5e, 0x28, 0x57, 0x2c,
+ 0xda, 0x01, 0x1d, 0xe8, 0x01, 0xf8, 0xf6, 0x37,
+ 0xe1, 0x34, 0x56, 0xae, 0x6e, 0xb1, 0xd4, 0xa2,
+ 0xc4, 0x02, 0xc1, 0xca, 0x96, 0xb0, 0x06, 0x72,
+ 0x2a, 0x27, 0xaa, 0xc8, 0xd5, 0x50, 0x81, 0x49,
+ 0x46, 0x33, 0xf8, 0xf7, 0x6b, 0xf4, 0x9c, 0x30,
+ 0x90, 0x50, 0xf6, 0x16, 0x76, 0x9d, 0xc6, 0x73,
+ 0xb5, 0xbc, 0x8a, 0xb6, 0x1d, 0x98, 0xcb, 0xce,
+ 0x36, 0x6f, 0x60, 0xec, 0x96, 0x49, 0x08, 0x85,
+ 0x5b, 0xc1, 0x8e, 0xb0, 0xea, 0x9e, 0x1f, 0xd6,
+ 0x27, 0x7f, 0xb6, 0xe0, 0x04, 0x12, 0xd2, 0x81
+};
+
+#ifndef PK11_MD5_DISABLE
+static unsigned char sigmd5[256] = {
+ 0xc0, 0x99, 0x90, 0xd6, 0xea, 0xc1, 0x5f, 0xc7,
+ 0x23, 0x60, 0xfc, 0x13, 0x3d, 0xcc, 0xda, 0x93,
+ 0x19, 0xf7, 0x22, 0xa9, 0x55, 0xbe, 0x70, 0x3c,
+ 0x87, 0x24, 0x8a, 0x7e, 0xa7, 0x59, 0x58, 0xd3,
+ 0x0e, 0x7c, 0x50, 0x3c, 0x81, 0x0f, 0x7a, 0x2b,
+ 0xb1, 0x94, 0x21, 0x87, 0xe4, 0x87, 0xcd, 0x2b,
+ 0xb9, 0xf1, 0xb8, 0x26, 0xc1, 0x02, 0xf4, 0x30,
+ 0x83, 0x41, 0x89, 0x61, 0xcc, 0x3d, 0xe3, 0x0f,
+ 0xec, 0x4a, 0x74, 0x95, 0x10, 0x65, 0xac, 0xd1,
+ 0xf5, 0x95, 0xe9, 0x99, 0xa8, 0x45, 0x98, 0x99,
+ 0xb5, 0xfd, 0x7a, 0x78, 0x80, 0xe5, 0x00, 0x33,
+ 0xa5, 0x54, 0xe5, 0xa3, 0xc0, 0x1b, 0x6c, 0xb9,
+ 0x77, 0x52, 0x6f, 0xe5, 0x85, 0xa8, 0xfa, 0x45,
+ 0x78, 0x49, 0x14, 0xa0, 0x10, 0x58, 0x40, 0x80,
+ 0x90, 0xc6, 0x55, 0x52, 0x6d, 0x46, 0x58, 0x50,
+ 0x3d, 0x5e, 0x40, 0x25, 0x51, 0x7c, 0xc4, 0x12,
+ 0x87, 0x2d, 0x7b, 0x10, 0xcd, 0x80, 0xec, 0x5d,
+ 0x27, 0x15, 0x09, 0x37, 0x1f, 0xa7, 0x86, 0x15,
+ 0xd1, 0xdd, 0xf1, 0x86, 0x1e, 0x42, 0x3a, 0xf9,
+ 0x5a, 0xed, 0x33, 0x07, 0xa9, 0x98, 0x08, 0x79,
+ 0xc5, 0xa4, 0x09, 0x95, 0x6e, 0x12, 0xfe, 0xee,
+ 0x49, 0x61, 0xe0, 0x99, 0xaa, 0x34, 0xa5, 0xca,
+ 0x82, 0xd3, 0x9b, 0x1c, 0x5b, 0x79, 0xf5, 0x0e,
+ 0x2c, 0x6c, 0x3b, 0x48, 0xd1, 0xbc, 0xd0, 0xda,
+ 0x73, 0xba, 0xe1, 0x81, 0x48, 0x27, 0x39, 0x2f,
+ 0x98, 0x77, 0x08, 0xb3, 0xf7, 0x38, 0x28, 0x6d,
+ 0x02, 0x56, 0xfa, 0x31, 0xbb, 0x14, 0x81, 0x6b,
+ 0x3c, 0x24, 0xa2, 0x68, 0x7a, 0x0a, 0x53, 0xbd,
+ 0x9d, 0x57, 0xd0, 0x99, 0x10, 0x28, 0x78, 0x69,
+ 0x31, 0x93, 0xa4, 0x73, 0x8d, 0x1a, 0xe4, 0xdc,
+ 0x0c, 0x15, 0xb8, 0x51, 0xd8, 0x66, 0x6a, 0x95,
+ 0x56, 0x17, 0x0a, 0x45, 0x72, 0xb5, 0xb8, 0xc4
+};
+#endif
+
+static unsigned char sigsha256[256] = {
+ 0x83, 0x53, 0x15, 0xfc, 0xca, 0xdb, 0xf6, 0x0d,
+ 0x53, 0x24, 0x5b, 0x5a, 0x8e, 0xd0, 0xbe, 0x5e,
+ 0xbc, 0xe8, 0x9e, 0x92, 0x3c, 0xfa, 0x93, 0x03,
+ 0xce, 0x2f, 0xc7, 0x6d, 0xd0, 0xbb, 0x9d, 0x06,
+ 0x83, 0xc6, 0xd3, 0xc0, 0xc1, 0x57, 0x9c, 0x82,
+ 0x17, 0x7f, 0xb5, 0xf8, 0x31, 0x18, 0xda, 0x46,
+ 0x05, 0x2c, 0xf8, 0xea, 0xaa, 0xcd, 0x99, 0x18,
+ 0xff, 0x23, 0x5e, 0xef, 0xf0, 0x87, 0x47, 0x6e,
+ 0x91, 0xfd, 0x19, 0x0b, 0x39, 0x19, 0x6a, 0xc8,
+ 0xdf, 0x71, 0x66, 0x8e, 0xa9, 0xa0, 0x79, 0x5c,
+ 0x2c, 0x52, 0x00, 0x61, 0x17, 0x86, 0x66, 0x03,
+ 0x52, 0xad, 0xec, 0x06, 0x53, 0xd9, 0x6d, 0xe3,
+ 0xe3, 0xea, 0x28, 0x15, 0xb3, 0x75, 0xf4, 0x61,
+ 0x7d, 0xed, 0x69, 0x2c, 0x24, 0xf3, 0x21, 0xb1,
+ 0x8a, 0xea, 0x60, 0xa2, 0x9e, 0x6a, 0xa6, 0x53,
+ 0x12, 0xf6, 0x5c, 0xef, 0xd7, 0x49, 0x4a, 0x02,
+ 0xe7, 0xf8, 0x64, 0x89, 0x13, 0xac, 0xd5, 0x1e,
+ 0x58, 0xff, 0xa1, 0x63, 0xdd, 0xa0, 0x1f, 0x44,
+ 0x99, 0x6a, 0x59, 0x7f, 0x35, 0xbd, 0xf1, 0xf3,
+ 0x7a, 0x28, 0x44, 0xe3, 0x4c, 0x68, 0xb1, 0xb3,
+ 0x97, 0x3c, 0x46, 0xe3, 0xc2, 0x12, 0x9e, 0x68,
+ 0x0b, 0xa6, 0x6c, 0x8f, 0x58, 0x48, 0x44, 0xa4,
+ 0xf7, 0xa7, 0xc2, 0x91, 0x8f, 0xbf, 0x00, 0xd0,
+ 0x01, 0x35, 0xd4, 0x86, 0x6e, 0x1f, 0xea, 0x42,
+ 0x60, 0xb1, 0x84, 0x27, 0xf4, 0x99, 0x36, 0x06,
+ 0x98, 0x12, 0x83, 0x32, 0x9f, 0xcd, 0x50, 0x5a,
+ 0x5e, 0xb8, 0x8e, 0xfe, 0x8d, 0x8d, 0x33, 0x2d,
+ 0x45, 0xe1, 0xc9, 0xdf, 0x2a, 0xd8, 0x38, 0x1d,
+ 0x95, 0xd4, 0x42, 0xee, 0x93, 0x5b, 0x0f, 0x1e,
+ 0x07, 0x06, 0x3a, 0x92, 0xf1, 0x59, 0x1d, 0x6e,
+ 0x1c, 0x31, 0xf3, 0xce, 0xa9, 0x1f, 0xad, 0x4d,
+ 0x76, 0x4d, 0x24, 0x98, 0xe2, 0x0e, 0x8c, 0x35
+};
+
+static unsigned char sigsha512[512] = {
+ 0x4e, 0x2f, 0x63, 0x42, 0xc5, 0xf3, 0x05, 0x4a,
+ 0xa6, 0x3a, 0x93, 0xa0, 0xd9, 0x33, 0xa0, 0xd1,
+ 0x46, 0x33, 0x42, 0xe8, 0x74, 0xeb, 0x3b, 0x10,
+ 0x82, 0xd7, 0xcf, 0x39, 0x23, 0xb3, 0xe9, 0x23,
+ 0x53, 0x87, 0x8c, 0xee, 0x78, 0xcb, 0xb3, 0xd9,
+ 0xd2, 0x6d, 0x1a, 0x7c, 0x01, 0x4f, 0xed, 0x8d,
+ 0xf2, 0x72, 0xe4, 0x6a, 0x00, 0x8a, 0x60, 0xa6,
+ 0xd5, 0x9c, 0x43, 0x6c, 0xef, 0x38, 0x0c, 0x74,
+ 0x82, 0x5d, 0x22, 0xaa, 0x87, 0x81, 0x90, 0x9c,
+ 0x64, 0x07, 0x9b, 0x13, 0x51, 0xe0, 0xa5, 0xc2,
+ 0x83, 0x78, 0x2b, 0x9b, 0xb3, 0x8a, 0x9d, 0x36,
+ 0x33, 0xbd, 0x0d, 0x53, 0x84, 0xae, 0xe8, 0x13,
+ 0x36, 0xf6, 0xdf, 0x96, 0xe9, 0xda, 0xc3, 0xd7,
+ 0xa9, 0x2f, 0xf3, 0x5e, 0x5f, 0x1f, 0x7f, 0x38,
+ 0x7e, 0x8d, 0xbe, 0x90, 0x5e, 0x13, 0xb2, 0x20,
+ 0xbb, 0x9d, 0xfe, 0xe1, 0x52, 0xce, 0xe6, 0x80,
+ 0xa7, 0x95, 0x24, 0x59, 0xe3, 0xac, 0x24, 0xc4,
+ 0xfa, 0x1c, 0x44, 0x34, 0x29, 0x8d, 0xb1, 0xd0,
+ 0xd9, 0x4c, 0xff, 0xc4, 0xdb, 0xca, 0xc4, 0x3f,
+ 0x38, 0xf9, 0xe4, 0xaf, 0x75, 0x0a, 0x67, 0x4d,
+ 0xa0, 0x2b, 0xb0, 0x83, 0xce, 0x53, 0xc4, 0xb9,
+ 0x2e, 0x61, 0xb6, 0x64, 0xe5, 0xb5, 0xe5, 0xac,
+ 0x9d, 0x51, 0xec, 0x58, 0x42, 0x90, 0x78, 0xf6,
+ 0x46, 0x96, 0xef, 0xb6, 0x97, 0xb7, 0x54, 0x28,
+ 0x1a, 0x4c, 0x29, 0xf4, 0x7a, 0x33, 0xc6, 0x07,
+ 0xfd, 0xec, 0x97, 0x36, 0x1d, 0x42, 0x88, 0x94,
+ 0x27, 0xc2, 0xa3, 0xe1, 0xd4, 0x87, 0xa1, 0x8a,
+ 0x2b, 0xff, 0x47, 0x60, 0xfe, 0x1f, 0xaf, 0xc2,
+ 0xeb, 0x17, 0xdd, 0x56, 0xc5, 0x94, 0x5c, 0xcb,
+ 0x23, 0xe5, 0x49, 0x4d, 0x99, 0x06, 0x02, 0x5a,
+ 0xfc, 0xfc, 0xdc, 0xee, 0x49, 0xbc, 0x47, 0x60,
+ 0xff, 0x6a, 0x63, 0x8b, 0xe1, 0x2e, 0xa3, 0xa7
+};
+
+ATF_TC(isc_rsa_verify);
+ATF_TC_HEAD(isc_rsa_verify, tc) {
+ atf_tc_set_md_var(tc, "descr", "RSA verify");
+}
+ATF_TC_BODY(isc_rsa_verify, tc) {
+ isc_result_t ret;
+ dns_fixedname_t fname;
+ isc_buffer_t buf;
+ dns_name_t *name;
+ dst_key_t *key = NULL;
+ dst_context_t *ctx = NULL;
+ isc_region_t r;
+
+ UNUSED(tc);
+
+ ret = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(ret, ISC_R_SUCCESS);
+
+ name = dns_fixedname_initname(&fname);
+ isc_buffer_constinit(&buf, "rsa.", 4);
+ isc_buffer_add(&buf, 4);
+ ret = dns_name_fromtext(name, &buf, NULL, 0, NULL);
+ ATF_REQUIRE_EQ(ret, ISC_R_SUCCESS);
+
+ ret = dst_key_fromfile(name, 29235, DST_ALG_RSASHA1,
+ DST_TYPE_PUBLIC, "./", mctx, &key);
+ ATF_REQUIRE_EQ(ret, ISC_R_SUCCESS);
+
+ /* RSASHA1 */
+
+ ret = dst_context_create3(key, mctx, DNS_LOGCATEGORY_DNSSEC,
+ false, &ctx);
+ ATF_REQUIRE_EQ(ret, ISC_R_SUCCESS);
+
+ r.base = d;
+ r.length = 10;
+ ret = dst_context_adddata(ctx, &r);
+ ATF_REQUIRE_EQ(ret, ISC_R_SUCCESS);
+
+ r.base = sigsha1;
+ r.length = 256;
+ ret = dst_context_verify(ctx, &r);
+ ATF_REQUIRE_EQ(ret, ISC_R_SUCCESS);
+
+ dst_context_destroy(&ctx);
+
+ /* RSAMD5 */
+
+#ifndef PK11_MD5_DISABLE
+ key->key_alg = DST_ALG_RSAMD5;
+
+ ret = dst_context_create3(key, mctx, DNS_LOGCATEGORY_DNSSEC,
+ false, &ctx);
+ ATF_REQUIRE_EQ(ret, ISC_R_SUCCESS);
+
+ r.base = d;
+ r.length = 10;
+ ret = dst_context_adddata(ctx, &r);
+ ATF_REQUIRE_EQ(ret, ISC_R_SUCCESS);
+
+ r.base = sigmd5;
+ r.length = 256;
+ ret = dst_context_verify(ctx, &r);
+ ATF_REQUIRE_EQ(ret, ISC_R_SUCCESS);
+
+ dst_context_destroy(&ctx);
+#endif
+
+ /* RSASHA256 */
+
+ key->key_alg = DST_ALG_RSASHA256;
+
+ ret = dst_context_create3(key, mctx, DNS_LOGCATEGORY_DNSSEC,
+ false, &ctx);
+ ATF_REQUIRE_EQ(ret, ISC_R_SUCCESS);
+
+ r.base = d;
+ r.length = 10;
+ ret = dst_context_adddata(ctx, &r);
+ ATF_REQUIRE_EQ(ret, ISC_R_SUCCESS);
+
+ r.base = sigsha256;
+ r.length = 256;
+ ret = dst_context_verify(ctx, &r);
+ ATF_REQUIRE_EQ(ret, ISC_R_SUCCESS);
+
+ dst_context_destroy(&ctx);
+
+ /* RSASHA512 */
+
+ key->key_alg = DST_ALG_RSASHA512;
+
+ ret = dst_context_create3(key, mctx, DNS_LOGCATEGORY_DNSSEC,
+ false, &ctx);
+ ATF_REQUIRE_EQ(ret, ISC_R_SUCCESS);
+
+ r.base = d;
+ r.length = 10;
+ ret = dst_context_adddata(ctx, &r);
+ ATF_REQUIRE_EQ(ret, ISC_R_SUCCESS);
+
+ r.base = sigsha512;
+ r.length = 256;
+ ret = dst_context_verify(ctx, &r);
+ ATF_REQUIRE_EQ(ret, ISC_R_SUCCESS);
+
+ dst_context_destroy(&ctx);
+
+
+ dst_key_free(&key);
+ dns_test_end();
+}
+#else
+ATF_TC(untested);
+ATF_TC_HEAD(untested, tc) {
+ atf_tc_set_md_var(tc, "descr", "skipping RSA test");
+}
+ATF_TC_BODY(untested, tc) {
+ UNUSED(tc);
+ atf_tc_skip("RSA not available");
+}
+#endif
+/*
+ * Main
+ */
+ATF_TP_ADD_TCS(tp) {
+#if defined(OPENSSL) || defined(PKCS11CRYPTO)
+ ATF_TP_ADD_TC(tp, isc_rsa_verify);
+#else
+ ATF_TP_ADD_TC(tp, untested);
+#endif
+ return (atf_no_error());
+}
+
diff --git a/lib/dns/tests/sigs_test.c b/lib/dns/tests/sigs_test.c
new file mode 100644
index 0000000..20fd4b6
--- /dev/null
+++ b/lib/dns/tests/sigs_test.c
@@ -0,0 +1,479 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <atf-c.h>
+
+#include <isc/util.h>
+
+#if defined(OPENSSL) || defined(PKCS11CRYPTO)
+#include <string.h>
+
+#include <dns/db.h>
+#include <dns/diff.h>
+#include <dns/dnssec.h>
+#include <dns/fixedname.h>
+#include <dns/name.h>
+#include <dns/rdata.h>
+#include <dns/rdatastruct.h>
+#include <dns/rdatatype.h>
+#include <dns/result.h>
+#include <dns/types.h>
+#include <dns/zone.h>
+
+#include <dst/dst.h>
+
+#include <isc/buffer.h>
+#include <isc/list.h>
+#include <isc/region.h>
+#include <isc/stdtime.h>
+#include <isc/result.h>
+#include <isc/types.h>
+
+#include "../zone_p.h"
+
+#include "dnstest.h"
+
+/*%
+ * Structure characterizing a single diff tuple in the dns_diff_t structure
+ * prepared by dns__zone_updatesigs().
+ */
+typedef struct {
+ dns_diffop_t op;
+ const char *owner;
+ dns_ttl_t ttl;
+ const char *type;
+} zonediff_t;
+
+#define ZONEDIFF_SENTINEL { 0, NULL, 0, NULL }
+
+/*%
+ * Structure defining a dns__zone_updatesigs() test.
+ */
+typedef struct {
+ const char *description; /* test description */
+ const zonechange_t *changes; /* array of "raw" zone changes */
+ const zonediff_t *zonediff; /* array of "processed" zone changes */
+} updatesigs_test_params_t;
+
+/*%
+ * Check whether the 'found' tuple matches the 'expected' tuple. 'found' is
+ * the 'index'th tuple output by dns__zone_updatesigs() in test 'test'.
+ */
+static void
+compare_tuples(const zonediff_t *expected, dns_difftuple_t *found,
+ const updatesigs_test_params_t *test, size_t index)
+{
+ char found_covers[DNS_RDATATYPE_FORMATSIZE] = { };
+ char found_type[DNS_RDATATYPE_FORMATSIZE] = { };
+ char found_name[DNS_NAME_FORMATSIZE];
+ isc_consttextregion_t typeregion;
+ dns_fixedname_t expected_fname;
+ dns_rdatatype_t expected_type;
+ dns_name_t *expected_name;
+ dns_rdata_rrsig_t rrsig;
+ isc_buffer_t typebuf;
+ isc_result_t result;
+
+ REQUIRE(expected != NULL);
+ REQUIRE(found != NULL);
+ REQUIRE(index > 0);
+
+ /*
+ * Check operation.
+ */
+ ATF_CHECK_EQ_MSG(expected->op, found->op,
+ "test \"%s\": tuple %zu: "
+ "expected op %d, found %d",
+ test->description, index,
+ expected->op, found->op);
+
+ /*
+ * Check owner name.
+ */
+ expected_name = dns_fixedname_initname(&expected_fname);
+ result = dns_name_fromstring(expected_name, expected->owner, 0, mctx);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ dns_name_format(&found->name, found_name, sizeof(found_name));
+ ATF_CHECK_MSG(dns_name_equal(expected_name, &found->name),
+ "test \"%s\": tuple %zu: "
+ "expected owner \"%s\", found \"%s\"",
+ test->description, index,
+ expected->owner, found_name);
+
+ /*
+ * Check TTL.
+ */
+ ATF_CHECK_EQ_MSG(expected->ttl, found->ttl,
+ "test \"%s\": tuple %zu: "
+ "expected TTL %u, found %u",
+ test->description, index,
+ expected->ttl, found->ttl);
+
+ /*
+ * Parse expected RR type.
+ */
+ typeregion.base = expected->type;
+ typeregion.length = strlen(expected->type);
+ result = dns_rdatatype_fromtext(&expected_type,
+ (isc_textregion_t*)&typeregion);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ /*
+ * Format found RR type for reporting purposes.
+ */
+ isc_buffer_init(&typebuf, found_type, sizeof(found_type));
+ result = dns_rdatatype_totext(found->rdata.type, &typebuf);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ /*
+ * Check RR type.
+ */
+ switch (expected->op) {
+ case DNS_DIFFOP_ADDRESIGN:
+ case DNS_DIFFOP_DELRESIGN:
+ /*
+ * Found tuple must be of type RRSIG.
+ */
+ ATF_CHECK_EQ_MSG(found->rdata.type, dns_rdatatype_rrsig,
+ "test \"%s\": tuple %zu: "
+ "expected type RRSIG, found %s",
+ test->description, index,
+ found_type);
+ if (found->rdata.type != dns_rdatatype_rrsig) {
+ break;
+ }
+ /*
+ * The signature must cover an RRset of type 'expected->type'.
+ */
+ result = dns_rdata_tostruct(&found->rdata, &rrsig, NULL);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ isc_buffer_init(&typebuf, found_covers, sizeof(found_covers));
+ result = dns_rdatatype_totext(rrsig.covered, &typebuf);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ ATF_CHECK_EQ_MSG(expected_type, rrsig.covered,
+ "test \"%s\": tuple %zu: "
+ "expected RRSIG to cover %s, found covers %s",
+ test->description, index,
+ expected->type, found_covers);
+ break;
+ default:
+ /*
+ * Found tuple must be of type 'expected->type'.
+ */
+ ATF_CHECK_EQ_MSG(expected_type, found->rdata.type,
+ "test \"%s\": tuple %zu: "
+ "expected type %s, found %s",
+ test->description, index,
+ expected->type, found_type);
+ break;
+ }
+}
+
+/*%
+ * Perform a single dns__zone_updatesigs() test defined in 'test'. All other
+ * arguments are expected to remain constant between subsequent invocations of
+ * this function.
+ */
+static void
+updatesigs_test(const updatesigs_test_params_t *test, dns_zone_t *zone,
+ dns_db_t *db, dst_key_t *zone_keys[], unsigned int nkeys,
+ isc_stdtime_t now)
+{
+ size_t tuples_expected, tuples_found, index;
+ dns_dbversion_t *version = NULL;
+ dns_diff_t raw_diff, zone_diff;
+ const zonediff_t *expected;
+ dns_difftuple_t *found;
+ isc_result_t result;
+
+ dns__zonediff_t zonediff = {
+ .diff = &zone_diff,
+ .offline = false,
+ };
+
+ REQUIRE(test != NULL);
+ REQUIRE(test->description != NULL);
+ REQUIRE(test->changes != NULL);
+ REQUIRE(zone != NULL);
+ REQUIRE(db != NULL);
+ REQUIRE(zone_keys != NULL);
+
+ /*
+ * Create a new version of the zone's database.
+ */
+ result = dns_db_newversion(db, &version);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ /*
+ * Create a diff representing the supplied changes.
+ */
+ result = dns_test_difffromchanges(&raw_diff, test->changes);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ /*
+ * Apply the "raw" diff to the new version of the zone's database as
+ * this is what dns__zone_updatesigs() expects to happen before it is
+ * called.
+ */
+ dns_diff_apply(&raw_diff, db, version);
+
+ /*
+ * Initialize the structure dns__zone_updatesigs() will modify.
+ */
+ dns_diff_init(mctx, &zone_diff);
+
+ /*
+ * Check whether dns__zone_updatesigs() behaves as expected.
+ */
+ result = dns__zone_updatesigs(&raw_diff, db, version, zone_keys, nkeys,
+ zone, now - 3600, now + 3600, now,
+ true, false, &zonediff);
+ ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS,
+ "test \"%s\": expected success, got %s",
+ test->description, isc_result_totext(result));
+ ATF_CHECK_MSG(ISC_LIST_EMPTY(raw_diff.tuples),
+ "test \"%s\": raw diff was not emptied",
+ test->description);
+ ATF_CHECK_MSG(!ISC_LIST_EMPTY(zone_diff.tuples),
+ "test \"%s\": zone diff was not created",
+ test->description);
+
+ /*
+ * Ensure that the number of tuples in the zone diff is as expected.
+ */
+
+ tuples_expected = 0;
+ for (expected = test->zonediff;
+ expected->owner != NULL;
+ expected++)
+ {
+ tuples_expected++;
+ }
+
+ tuples_found = 0;
+ for (found = ISC_LIST_HEAD(zone_diff.tuples);
+ found != NULL;
+ found = ISC_LIST_NEXT(found, link))
+ {
+ tuples_found++;
+ }
+
+ ATF_REQUIRE_EQ_MSG(tuples_expected, tuples_found,
+ "test \"%s\": "
+ "expected %zu tuples in output, found %zu",
+ test->description,
+ tuples_expected, tuples_found);
+
+ /*
+ * Ensure that every tuple in the zone diff matches expectations.
+ */
+ expected = test->zonediff;
+ index = 1;
+ for (found = ISC_LIST_HEAD(zone_diff.tuples);
+ found != NULL;
+ found = ISC_LIST_NEXT(found, link))
+ {
+ compare_tuples(expected, found, test, index);
+ expected++;
+ index++;
+ }
+
+ /*
+ * Apply changes to zone database contents and clean up.
+ */
+ dns_db_closeversion(db, &version, true);
+ dns_diff_clear(&zone_diff);
+ dns_diff_clear(&raw_diff);
+}
+
+ATF_TC(updatesigs);
+ATF_TC_HEAD(updatesigs, tc) {
+ atf_tc_set_md_var(tc, "descr", "dns__zone_updatesigs() tests");
+}
+ATF_TC_BODY(updatesigs, tc) {
+ dst_key_t *zone_keys[DNS_MAXZONEKEYS];
+ dns_zone_t *zone = NULL;
+ dns_db_t *db = NULL;
+ isc_result_t result;
+ unsigned int nkeys;
+ isc_stdtime_t now;
+ size_t i;
+
+ result = dns_test_begin(NULL, true);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ /*
+ * Prepare a zone along with its signing keys.
+ */
+
+ result = dns_test_makezone("example", &zone, NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_test_loaddb(&db, dns_dbtype_zone, "example",
+ "testdata/master/master18.data");
+ ATF_REQUIRE_EQ(result, DNS_R_SEENINCLUDE);
+
+ result = dns_zone_setkeydirectory(zone, "testkeys");
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ isc_stdtime_get(&now);
+ result = dns__zone_findkeys(zone, db, NULL, now, mctx, DNS_MAXZONEKEYS,
+ zone_keys, &nkeys);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ ATF_REQUIRE_EQ(nkeys, 2);
+
+ /*
+ * Define the tests to be run. Note that changes to zone database
+ * contents introduced by each test are preserved between tests.
+ */
+
+ const zonechange_t changes_add[] = {
+ { DNS_DIFFOP_ADD, "foo.example", 300, "TXT", "foo" },
+ { DNS_DIFFOP_ADD, "bar.example", 600, "TXT", "bar" },
+ ZONECHANGE_SENTINEL,
+ };
+ const zonediff_t zonediff_add[] = {
+ { DNS_DIFFOP_ADDRESIGN, "foo.example", 300, "TXT" },
+ { DNS_DIFFOP_ADD, "foo.example", 300, "TXT" },
+ { DNS_DIFFOP_ADDRESIGN, "bar.example", 600, "TXT" },
+ { DNS_DIFFOP_ADD, "bar.example", 600, "TXT" },
+ ZONEDIFF_SENTINEL,
+ };
+ const updatesigs_test_params_t test_add = {
+ .description = "add new RRsets",
+ .changes = changes_add,
+ .zonediff = zonediff_add,
+ };
+
+ const zonechange_t changes_append[] = {
+ { DNS_DIFFOP_ADD, "foo.example", 300, "TXT", "foo1" },
+ { DNS_DIFFOP_ADD, "foo.example", 300, "TXT", "foo2" },
+ ZONECHANGE_SENTINEL,
+ };
+ const zonediff_t zonediff_append[] = {
+ { DNS_DIFFOP_DELRESIGN, "foo.example", 300, "TXT" },
+ { DNS_DIFFOP_ADDRESIGN, "foo.example", 300, "TXT" },
+ { DNS_DIFFOP_ADD, "foo.example", 300, "TXT" },
+ { DNS_DIFFOP_ADD, "foo.example", 300, "TXT" },
+ ZONEDIFF_SENTINEL,
+ };
+ const updatesigs_test_params_t test_append = {
+ .description = "append multiple RRs to an existing RRset",
+ .changes = changes_append,
+ .zonediff = zonediff_append,
+ };
+
+ const zonechange_t changes_replace[] = {
+ { DNS_DIFFOP_DEL, "bar.example", 600, "TXT", "bar" },
+ { DNS_DIFFOP_ADD, "bar.example", 600, "TXT", "rab" },
+ ZONECHANGE_SENTINEL,
+ };
+ const zonediff_t zonediff_replace[] = {
+ { DNS_DIFFOP_DELRESIGN, "bar.example", 600, "TXT" },
+ { DNS_DIFFOP_ADDRESIGN, "bar.example", 600, "TXT" },
+ { DNS_DIFFOP_DEL, "bar.example", 600, "TXT" },
+ { DNS_DIFFOP_ADD, "bar.example", 600, "TXT" },
+ ZONEDIFF_SENTINEL,
+ };
+ const updatesigs_test_params_t test_replace = {
+ .description = "replace an existing RRset",
+ .changes = changes_replace,
+ .zonediff = zonediff_replace,
+ };
+
+ const zonechange_t changes_delete[] = {
+ { DNS_DIFFOP_DEL, "bar.example", 600, "TXT", "rab" },
+ ZONECHANGE_SENTINEL,
+ };
+ const zonediff_t zonediff_delete[] = {
+ { DNS_DIFFOP_DELRESIGN, "bar.example", 600, "TXT" },
+ { DNS_DIFFOP_DEL, "bar.example", 600, "TXT" },
+ ZONEDIFF_SENTINEL,
+ };
+ const updatesigs_test_params_t test_delete = {
+ .description = "delete an existing RRset",
+ .changes = changes_delete,
+ .zonediff = zonediff_delete,
+ };
+
+ const zonechange_t changes_mixed[] = {
+ { DNS_DIFFOP_ADD, "baz.example", 900, "TXT", "baz1" },
+ { DNS_DIFFOP_ADD, "baz.example", 900, "A", "127.0.0.1" },
+ { DNS_DIFFOP_ADD, "baz.example", 900, "TXT", "baz2" },
+ { DNS_DIFFOP_ADD, "baz.example", 900, "AAAA", "::1" },
+ ZONECHANGE_SENTINEL,
+ };
+ const zonediff_t zonediff_mixed[] = {
+ { DNS_DIFFOP_ADDRESIGN, "baz.example", 900, "TXT" },
+ { DNS_DIFFOP_ADD, "baz.example", 900, "TXT" },
+ { DNS_DIFFOP_ADD, "baz.example", 900, "TXT" },
+ { DNS_DIFFOP_ADDRESIGN, "baz.example", 900, "A" },
+ { DNS_DIFFOP_ADD, "baz.example", 900, "A" },
+ { DNS_DIFFOP_ADDRESIGN, "baz.example", 900, "AAAA" },
+ { DNS_DIFFOP_ADD, "baz.example", 900, "AAAA" },
+ ZONEDIFF_SENTINEL,
+ };
+ const updatesigs_test_params_t test_mixed = {
+ .description = "add different RRsets with common owner name",
+ .changes = changes_mixed,
+ .zonediff = zonediff_mixed,
+ };
+
+ const updatesigs_test_params_t *tests[] = {
+ &test_add,
+ &test_append,
+ &test_replace,
+ &test_delete,
+ &test_mixed,
+ };
+
+ /*
+ * Run tests.
+ */
+ for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
+ updatesigs_test(tests[i], zone, db, zone_keys, nkeys, now);
+ }
+
+ /*
+ * Clean up.
+ */
+ for (i = 0; i < nkeys; i++) {
+ dst_key_free(&zone_keys[i]);
+ }
+ dns_db_detach(&db);
+ dns_zone_detach(&zone);
+
+ dns_test_end();
+}
+#else
+ATF_TC(untested);
+ATF_TC_HEAD(untested, tc) {
+ atf_tc_set_md_var(tc, "descr", "skipping dns__zone_updatesigs() test");
+}
+ATF_TC_BODY(untested, tc) {
+ UNUSED(tc);
+ atf_tc_skip("DNSSEC support not compiled in");
+}
+#endif
+
+ATF_TP_ADD_TCS(tp) {
+#if defined(OPENSSL) || defined(PKCS11CRYPTO)
+ ATF_TP_ADD_TC(tp, updatesigs);
+#else
+ ATF_TP_ADD_TC(tp, untested);
+#endif
+
+ return (atf_no_error());
+}
diff --git a/lib/dns/tests/testdata/db/data.db b/lib/dns/tests/testdata/db/data.db
new file mode 100644
index 0000000..9ac043e
--- /dev/null
+++ b/lib/dns/tests/testdata/db/data.db
@@ -0,0 +1,20 @@
+; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+;
+; This Source Code Form is subject to the terms of the Mozilla Public
+; License, v. 2.0. If a copy of the MPL was not distributed with this
+; file, You can obtain one at http://mozilla.org/MPL/2.0/.
+;
+; See the COPYRIGHT file distributed with this work for additional
+; information regarding copyright ownership.
+
+$TTL 1000
+@ in soa localhost. postmaster.localhost. (
+ 1993050801 ;serial
+ 3600 ;refresh
+ 1800 ;retry
+ 604800 ;expiration
+ 3600 ) ;minimum
+a in ns ns.vix.com.
+a in ns ns2.vix.com.
+a in ns ns3.vix.com.
+b in a 1.2.3.4
diff --git a/lib/dns/tests/testdata/dbiterator/zone1.data b/lib/dns/tests/testdata/dbiterator/zone1.data
new file mode 100644
index 0000000..81c0abe
--- /dev/null
+++ b/lib/dns/tests/testdata/dbiterator/zone1.data
@@ -0,0 +1,30 @@
+; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+;
+; This Source Code Form is subject to the terms of the Mozilla Public
+; License, v. 2.0. If a copy of the MPL was not distributed with this
+; file, You can obtain one at http://mozilla.org/MPL/2.0/.
+;
+; See the COPYRIGHT file distributed with this work for additional
+; information regarding copyright ownership.
+
+$TTL 600
+@ in soa localhost. postmaster.localhost. (
+ 2011080901 ;serial
+ 3600 ;refresh
+ 1800 ;retry
+ 604800 ;expiration
+ 600 ) ;minimum
+ in ns ns
+ in ns ns2
+ns in a 10.0.0.1
+ns2 in a 10.0.0.2
+
+a in txt "test"
+b in txt "test"
+c in txt "test"
+d.e.f in txt "test"
+e in txt "test"
+f.g.h in txt "test"
+f.g.i in txt "test"
+f.g.j in txt "test"
+k in txt "test"
diff --git a/lib/dns/tests/testdata/dbiterator/zone2.data b/lib/dns/tests/testdata/dbiterator/zone2.data
new file mode 100644
index 0000000..7265c27
--- /dev/null
+++ b/lib/dns/tests/testdata/dbiterator/zone2.data
@@ -0,0 +1,319 @@
+; File written on Mon Aug 15 16:51:56 2011
+; dnssec_signzone version 9.7.3rc1
+test. 600 IN SOA localhost. postmaster.localhost. (
+ 2011080901 ; serial
+ 3600 ; refresh (1 hour)
+ 1800 ; retry (30 minutes)
+ 604800 ; expire (1 week)
+ 600 ; minimum (10 minutes)
+ )
+ 600 RRSIG SOA 7 1 600 20110914225156 (
+ 20110815225156 39833 test.
+ IoQPcpx+Y2btVBBdM2H/9ppRMjphB1thwrdh
+ midhKH+MXDAauUIENucugi3zLsc1o2ke8LnQ
+ v3lCLd/bb5MD1otuS8vOw1GWEFhXOUBZU6wS
+ QwEIcG4BiSlz7/GvOlRa2znkOmZ3c8bD/J3Y
+ XUWDI3BEDPgrZqfxEvoMyPEWjO8= )
+ 600 NS ns.test.
+ 600 NS ns2.test.
+ 600 RRSIG NS 7 1 600 20110914225156 (
+ 20110815225156 39833 test.
+ OgEimhmFIAqlH0hyQy3pTsveBHKyqs9WfO1S
+ uDPRj3DFgFEAjoY473T8GxG2C+jTVL/UMVcb
+ BTZ8wIAiUHhqKLcmr0q/1X+kNUs7tNi+6oMn
+ /jxaOuRL6c8Kf2gl2t4g6JTwQqLQhUHTfQP+
+ bEfKUr75VsVfxCQZIHlZ3/AlxZM= )
+ 600 DNSKEY 256 3 7 (
+ AwEAAc0FzrE7jUiaKIGZpIaFE8E989topAJN
+ dWIQUQ7BSKabmpBP2M+SXHwIiQ/yC25iqudO
+ IxjRcK7nHB1VoP84xU2oMj6eeSqQHf/bYaji
+ Y8IfR7lgrzoDWzq+0rtnKMJc/JM8SMkcoBAS
+ llvxarDJTZheZjlrCvhpRJC+FAkBsx81
+ ) ; key id = 39833
+ 600 DNSKEY 257 3 7 (
+ AwEAAc55LPDhBLqfDUpjYYbBt+N63CiZtKrD
+ UDGeFAerbw0MWIUi3PgMr7yGVrj8e5Qjp9UN
+ zBUax6NdhlYVtFA8CwMTXGBjxgyqUoWpce08
+ lswxfE70BpgUA6w5efs0/mYtX9/A76etCaSI
+ oNH2vfa47BCdCPDfC1uTgyeuNuDvhszHaSiD
+ 8OY7tLa/voecUlq38sdqi2raf2DvgOm7rdFa
+ reXOS/WIj7zd4XYrV1JGthxOMVlQ7zdv9rVd
+ UNUIF2d4hwCZJQr0ejhmvB3m/DuNmNOPYmnv
+ KTmLSE+IJ6baqYvKOVxwV+SaCnuJEjv+3Yrx
+ 8WQYD/iS9WBhC9FUit0dy+0=
+ ) ; key id = 57183
+ 600 RRSIG DNSKEY 7 1 600 20110914225156 (
+ 20110815225156 39833 test.
+ xPV+bSGUlbxA5MKBeeRbwUDh3Qc+dm77+OHQ
+ BHIr1L8/kRP5o5J7MqPA37kea6nhyltYf9xM
+ RsxyiaBGUUeLyWg/q6hTtkNgAHifOPAhiDz8
+ AJDSTdSsq9RVtjdobAD0jyzz9sWnB+TPSOmj
+ Nlyd7VtPVEuSYljgawwfBBO3Kho= )
+ 600 RRSIG DNSKEY 7 1 600 20110914225156 (
+ 20110815225156 57183 test.
+ S3jkC7AvyFc4ShfHt6AWgS4zpx9DzWHBK9gV
+ 2H23OJzy8H1At/CjKxWVHLJ/io+ygryVnt/I
+ 47Jyhh9i43TnXj8il475YsweGnXGZSorrcXA
+ 3IsD2lOuRYnp3yetxe2ZrMGNDqqImE6X4x1a
+ UJI0cbE2UMZfUt8Rm5USiGzwAEgFD1OXxvMD
+ UT3flyp+Ote9FConK8gewV4wlJuBFemWT7BZ
+ lUYnoqfuAeEn2+1pIBS0iA0LNFjNBaEgtcjo
+ QeweN32yKoApau47Dl/Klw7KFT8+PLZ0QPbt
+ XAkJU7q94Q5aucDuHCSCTCc+2vZxdEnXKvRY
+ rfLuG8r/V5Kn+1iYrQ== )
+ 0 NSEC3PARAM 1 0 10 -
+ 0 RRSIG NSEC3PARAM 7 1 0 20110914225156 (
+ 20110815225156 39833 test.
+ kghSSeP8AZiQ/zmxgxAyG0itoUMo5adG5pxD
+ p8T3ZmbxEUSyG5acxBFkmeY39wVU0Cda8tWc
+ HHrMbB5e2GN8z6xJ0A4rVyXfKSYJSz+iKWfk
+ 7sOFRjd8OLYE3di6PwIpk6ORUiRPMFLDQCH0
+ Q27hLsSoKyd50orKKI+ncjz7WzU= )
+a.test. 600 IN TXT "test"
+ 600 RRSIG TXT 7 2 600 20110914225156 (
+ 20110815225156 39833 test.
+ UEVOlnL6CDRNCfk/Xge2oaGYCV1+ewwi5zJ0
+ CX4DdwiNEkItL4HgBe8xXfxgFC3qySdsSYPE
+ 1krdFyIkAclMCwHECd1UwZbGlMTEUGrE1KOB
+ 8vQY+OhIV9TAhqNwnjbu7s2ZdNUv3wiUPcfk
+ hCJ4rzP6yeV2inLwZulXnhxb6Pk= )
+b.test. 600 IN TXT "test"
+ 600 RRSIG TXT 7 2 600 20110914225156 (
+ 20110815225156 39833 test.
+ HcyQlO9io6Rc5e4vVqlRmK5PacOaFQJmdERG
+ 5Aobpgm1FuCLC7F+IMZ0d1XvBWnsw9iDzV43
+ UKzTGqUSmDiSBzs4QzHlacGickIW8EOV4xyJ
+ +mcJ0FZh4YNbkt6CiX+8SF6IxfCMhRMjpSsK
+ rWqJMG3LXkI6W9stShzsYAFBOzQ= )
+e.test. 600 IN TXT "test"
+ 600 RRSIG TXT 7 2 600 20110914225156 (
+ 20110815225156 39833 test.
+ jUn5FGRTL9OcFU7tvfkUnSwY8jA+8JynE0hi
+ ZJbYXDU5CiWGmR2B3yPHxUCewRqouyVCV8bc
+ xZsSuBxvcdYKryYDbjsmB83GlSEuxE9J7XZs
+ 8SxUP8PobLVqzXgEZS/XRU2G+R915ZDP9/iL
+ z9oYwc9TkeyXbp8J/ZsH88tG980= )
+c.test. 600 IN TXT "test"
+ 600 RRSIG TXT 7 2 600 20110914225156 (
+ 20110815225156 39833 test.
+ cRxAj45oFDDCd8xQXxD1F0Qq8XeBWAj8EYS3
+ 7nFXAgAy8sTczFvYCNGj79o7BALJwM4vc/wx
+ 6rjsiO/sHgfTMEBDq6lH9Wql72uhwavI2SrL
+ /h/wBP5q4BXlQ4xp6cLhhdifOWhNTvLP+Fe5
+ U6yjvqneiKspze9SiFbcmRDiJds= )
+d.e.f.test. 600 IN TXT "test"
+ 600 RRSIG TXT 7 4 600 20110914225156 (
+ 20110815225156 39833 test.
+ ENjCzr/P9rJmj5OJLzYwWtHtBg2Uz+qJDucz
+ I97Pq9F819/c5sxNfT4hgICCw6ZfT4ffbzye
+ fFJ0JVrh2cYOzu68ozlgek/Uml1UW0pDQVdI
+ s4zEgp4XK9wXUxtWChSqp5YXMdeHegZFu32i
+ IMNTbJDudwYSwhr2FyG92ZRi8Y8= )
+f.g.h.test. 600 IN TXT "test"
+ 600 RRSIG TXT 7 4 600 20110914225156 (
+ 20110815225156 39833 test.
+ HT7iocFsfDjeX6j9RJdE3xfVGkIxhajFHgM/
+ T/mJj/al4HKV6Ajia8DhpdfDrgM2m7r+Pgcn
+ FSIstfebQsuFCnHX/gIalDND/grHKsetQnMP
+ Y7O4QLsRnTV53fdlqQ4eT+jBW6fzJdGySVN+
+ bg6kNJZS8DebjmlKtZz7tXjkP+4= )
+f.g.i.test. 600 IN TXT "test"
+ 600 RRSIG TXT 7 4 600 20110914225156 (
+ 20110815225156 39833 test.
+ kHJJeNSL1rz4QRYqOzhGMQl1yIdio7l8Lg8H
+ f0TsvFLa6BudVtwKUm+Kz2QiDn7/Lew8w0KX
+ vVHxX/Vwl3Ixk54YgMKLNogz2TEvnh/VGiS7
+ 8r0oSUrg0CFd+xDfxnLeRqX5NNfMuSJap5WH
+ Aw7IVeRjXDwJFYnytMEnTrhHHHg= )
+f.g.j.test. 600 IN TXT "test"
+ 600 RRSIG TXT 7 4 600 20110914225156 (
+ 20110815225156 39833 test.
+ lIEHEhDFhOWK8W/F2xWELU2p/X77S2KTivm9
+ sY4k3RPsLNHE7p+lF8p72Lcb79rtltnoVYtE
+ pTIiaUcmgGwfaI4cwfXbeuEgnuTiLg7Xrefx
+ 3GT86Q+8gfgbMXUmRA/eouWZhCOaYJN99gYz
+ urzDMiRLYmILHmLlnvo82SgXeuk= )
+k.test. 600 IN TXT "test"
+ 600 RRSIG TXT 7 2 600 20110914225156 (
+ 20110815225156 39833 test.
+ wC3zgYWsuLga8Vu3QFu/Ci8SzRbA5bvjSmDj
+ NzcpjU5cvJBxtgzatCr02AaUC94bI0JzNrEB
+ nFyWCYw55lyy+bAHU1u05UcQmz0n5yxkvmHX
+ i8ZjMyQkAvNKodJHaFQqUKKIDuSHD2EziKqg
+ eNn55YRS11ihkODehUVNl7TnYeA= )
+ns.test. 600 IN A 10.0.0.1
+ 600 RRSIG A 7 2 600 20110914225156 (
+ 20110815225156 39833 test.
+ VyK/WlQ6ikXdjF/arGzyAyYhOc8IYNBp4QLW
+ gtYjvbjIcV5+9JINWmUs61VjJ14nES1sI0xb
+ 9vQJuiPXTM1awUAnvOKLhaX6fbJaEiR1w6Cf
+ RT5QKBMxNBKVStqdabHcigY4DUuc1PQk1vCw
+ yMUJt3nHNVMZk+XAycNHzBeYjik= )
+ns2.test. 600 IN A 10.0.0.2
+ 600 RRSIG A 7 2 600 20110914225156 (
+ 20110815225156 39833 test.
+ CX6UlZL+5NQJViKfbe/E3uIJk/wjUzoiHBhY
+ B6gS8nxZzlRPdTTXyMZoRa4etTZEbrRjnyXk
+ 1rP47faCUwbh//XqukN9f7FZ4Y39NpPS2XpX
+ 0Lx6M93Jz46lbzmseMFs2YmNMzzhN4uhRvl/
+ 8gPtYsn9KMXnAlFfa4XrE5LNVyY= )
+1F3JQ6EANHNHOCMUPQTVNM339VDTR51C.test. 600 IN NSEC3 1 0 10 - 7QKPELF33JOK9BVJ7CKE99AHG40B0SH7 A RRSIG
+ 600 RRSIG NSEC3 7 2 600 20110914225156 (
+ 20110815225156 39833 test.
+ w7aS12lxLNh+G1B/2kEq1BO6IzYvyC8n/MGV
+ 0jvFnapNXGZMPrPxGeO2wkw1JXepuXCv98be
+ M4SjQywaH+VP6ZMTIfjxRxtcCM+aLAFhiz0l
+ /MILEkjemmxjAfvV7emRVMwCGcoGI7qC3Xxq
+ q5g8EzJiYyTCOnI5LKRggn97wGg= )
+7QKPELF33JOK9BVJ7CKE99AHG40B0SH7.test. 600 IN NSEC3 1 0 10 - 94Q15K1V1VE5F87EI37T2B9A39EEC368 TXT RRSIG
+ 600 RRSIG NSEC3 7 2 600 20110914225156 (
+ 20110815225156 39833 test.
+ J4ObL3p4eN0jWh06M+rX2SSPANQoKfnosElB
+ KcKE7fLqEjKK7N6Yh6KUlbEP25tfeZ7W6GBJ
+ b7q6Nh0Ax8fYdc/6JVvmxcwWcx5Lw1TfITGB
+ ttFntJlbp1A8lwP3pn8Ksql1X2ogh78AsgTb
+ X5kmXVukC1oEzt98EAa/V/an8QA= )
+CS8M3UVG0UJDR6USBES4U9SNUGQI2RJE.test. 600 IN NSEC3 1 0 10 - ETEQB5V431INUIIE547FKSOF7O4DJ62J A RRSIG
+ 600 RRSIG NSEC3 7 2 600 20110914225156 (
+ 20110815225156 39833 test.
+ Vyd/2b0S15fACJ8TiPXKtScV9A/ZztVumZAm
+ o2S6jaVJKWik+8orDW+WiJ4/PEl26PK2m1uv
+ HD2beuUCHj9EnYkN/dzL3Bsc302qr9xqsh0q
+ VFS2moznoNG415ZV3vgYR7L9DAp43ZeFuw6I
+ 7sr21hLYLUeo31xBsJg7RlOL+4s= )
+ETEQB5V431INUIIE547FKSOF7O4DJ62J.test. 600 IN NSEC3 1 0 10 - F8G1MB0JUEU3FBI11CAVFIPGEA3POOIM
+ 600 RRSIG NSEC3 7 2 600 20110914225156 (
+ 20110815225156 39833 test.
+ oOHs1eb3JYeOMOnzE2PS6NIXBNzSoTYPIxo/
+ P0d/ihsLKra3yNJNPTlu4kf+FZoNYAGtMK/D
+ 6dZWFvtdswDdi2C5WSgsanuHqXq5Lr3A1nCe
+ cQI5PO4RrLymB+MtYg15CNKcnc0WmJO8deSR
+ WzNOarC+Iz1Xj3FkKDS4FFr+02Q= )
+94Q15K1V1VE5F87EI37T2B9A39EEC368.test. 600 IN NSEC3 1 0 10 - CS8M3UVG0UJDR6USBES4U9SNUGQI2RJE
+ 600 RRSIG NSEC3 7 2 600 20110914225156 (
+ 20110815225156 39833 test.
+ K0PvN7YtHQ63x/x2yXXa2S9GBGuTNJywDZ8M
+ wyMSwytCb9mn4hnKD5mJHaXGTw3YX7usbnEO
+ ce6hiJdN/VhMfbRMOvUpgyblOj4kXiYVZY1a
+ SyycfugK/Hu1j4az7lIhhnnx58GChA6mg8Vx
+ 3Uz6cNDDCSTBTl09NyeUUrKWsHQ= )
+FBH6B0LHT9PPQB1P98D228HA1H52L8PO.test. 600 IN NSEC3 1 0 10 - JGU2L7C3LKLHAKC5RHUOORTI2DCKK3KL
+ 600 RRSIG NSEC3 7 2 600 20110914225156 (
+ 20110815225156 39833 test.
+ giXRE+4ZeIzDrhx1XkFSpIKGFd3UGzlrLZnO
+ Ur9nMUfwvU5A3fitEkdayo3ZDH7MQGpSotaH
+ ReiFXx3Z6Hm2NIN/RHYZQr9e0vbMYSjkANdu
+ HWBA1SrSq5SHyuy970mPd4jfTHiABCo6fJGB
+ ykGClZGou0WSaB+Ak19fMbeQ2Wo= )
+JGU2L7C3LKLHAKC5RHUOORTI2DCKK3KL.test. 600 IN NSEC3 1 0 10 - KFMJ88CKMKUQQJE59IKFBOLLLD4DF55H TXT RRSIG
+ 600 RRSIG NSEC3 7 2 600 20110914225156 (
+ 20110815225156 39833 test.
+ BHTDUgZdWNLgz3xHYMqvlWK/IJ0xrXESoREc
+ 6D3sO9bcLTMYPO9t80itOlipwp4AmaVOBXPt
+ cKSdgsUXDEtHqNSxtGbNr5xQ+Aqsep0GX71V
+ HkcIuiNdTUw83dkajCHMkmQCbEjp9mbdiTmS
+ haNW2EsscldfaS1aq5tYUhCT3l4= )
+L993U6VC0DUV5QJ8TRPD2IQLM8FJ7AT9.test. 600 IN NSEC3 1 0 10 - LSMRLLNBQGGK8J6V40KLM2LG5TE4FS0P
+ 600 RRSIG NSEC3 7 2 600 20110914225156 (
+ 20110815225156 39833 test.
+ vE7K0Nrju4qLFDYkIyMY5bIMT0wu8MJdxL6u
+ 7WVA4HepccKQcUnvVoBAcrA9+MUeteyrad8Y
+ SJvQIt7sz5t7FViWSq5IMPVPujWtW5J30LhJ
+ mOLd1KmnFWoVthJ1oFNzBM80A60seKNnEw1M
+ lV6Y+v0gNYIQensUb9w6SVMTpxE= )
+F8G1MB0JUEU3FBI11CAVFIPGEA3POOIM.test. 600 IN NSEC3 1 0 10 - FA1T7MKUUV9SD4VDBJQ3GRFK1IDTCKL7
+ 600 RRSIG NSEC3 7 2 600 20110914225156 (
+ 20110815225156 39833 test.
+ DkL9ONc0vpsKdG20ol8XPAaVfLb7kf1wnKbR
+ rQUB1trGSHm/Igo06of43zm9J+56htFJg1xD
+ I2de0sCUBQYyHVBBDiBAd1g+ZvcpUlLP0w8M
+ NxMviMiG/WQAdGXHwYfUimwMWD7gNGl1m05H
+ HwYmzGs+d1bClDNBrFhdfdL2+iA= )
+LSMRLLNBQGGK8J6V40KLM2LG5TE4FS0P.test. 600 IN NSEC3 1 0 10 - LUAN2Q3I2OCVSD41MP08HNA9JP22D38K
+ 600 RRSIG NSEC3 7 2 600 20110914225156 (
+ 20110815225156 39833 test.
+ ZgiWuMqodQuhwuAF6CIiJTsdRahi+poOiZAM
+ WXNP0wXfdptcG2uhbdDwy+0crhe3tuybhwcb
+ CuiaQUh0XNPhgF+qmXpGobaqBhCEvCF4K9qY
+ OCIoMfsI1pIBVbMw0+YXVarFZ8+mfNU/+6n6
+ yy2+1nCg3k4XR2Dpv4CeDBfcAuM= )
+NAL1UIEBM38NKMN6RQOKE8T781IA7UKI.test. 600 IN NSEC3 1 0 10 - OUSGP0LO9FGAROHDULQVSTI3OLQIBB39 TXT RRSIG
+ 600 RRSIG NSEC3 7 2 600 20110914225156 (
+ 20110815225156 39833 test.
+ x8JiXPI+EXHz8ZO/VW0/+9wWsBNqeSMxXZIV
+ ibOnogSg7Wi7Yq1xftKC2+xEevNxSZnBibEy
+ Sgro5xKTf0n7pD9hHVBLoYmOOnbXY3QNQ2EQ
+ y3LdPT355WmwVddVOOxNpNRp2zQyqg7BhVA3
+ wxY7tyVQd4x1+95ATUQBnFditdE= )
+KFMJ88CKMKUQQJE59IKFBOLLLD4DF55H.test. 600 IN NSEC3 1 0 10 - L993U6VC0DUV5QJ8TRPD2IQLM8FJ7AT9 TXT RRSIG
+ 600 RRSIG NSEC3 7 2 600 20110914225156 (
+ 20110815225156 39833 test.
+ KQPaN2Ecebifbl4Bz5Yo0x2DgGmZiVhpSydm
+ oy/5NtMjt7G472JrKlqByap+VxW0bpzo3IER
+ 3P8Dsv7pfBD4/Cl5sFqwZL7wYy7RB4dQLVCi
+ Pepc/Mr3gR2XmL91fpGttMj5jGscnVQJCyFa
+ obzhsVaVImUQZFDPb0UQUHwIhOA= )
+LUAN2Q3I2OCVSD41MP08HNA9JP22D38K.test. 600 IN NSEC3 1 0 10 - NAL1UIEBM38NKMN6RQOKE8T781IA7UKI TXT RRSIG
+ 600 RRSIG NSEC3 7 2 600 20110914225156 (
+ 20110815225156 39833 test.
+ NJ+X3d0qh2+fbSnG0iQPxAeDIOzX5NTmY9fS
+ x7IO/DDcgUhPvl1YYdz5J999cec1zzOKp10J
+ YbsIAzg0w/Y4D4CBUw3IkcOrUFOODb6eJQGb
+ rVFRqmp3BUP4qOAWUZvx4oQ0KG4K/h/KJMbU
+ Vcdl7PF7G5O5hMyR9UWg4zal7Sk= )
+OUSGP0LO9FGAROHDULQVSTI3OLQIBB39.test. 600 IN NSEC3 1 0 10 - PQQ28M3U2MM08GGFV3JKR76G2H9IUJPC TXT RRSIG
+ 600 RRSIG NSEC3 7 2 600 20110914225156 (
+ 20110815225156 39833 test.
+ A/qxYrSE/smBGbST8j8eGPCrRnwvVa25kDha
+ IuA3nv0vzXhFvlruc9f0HRGwsq6A2pw3I5W+
+ xo2/JxsNyFOotdwaDDEBzqPkJmrzupxQS4Hm
+ rHSLnRnNw4QzvzNjAGWMYAoe3OeHC47wmAtI
+ qE91EHZTlPP28CUXOMo+7sCaOa8= )
+U0UVS2SUP89P2TM3PJO4TC1GPJ2O6519.test. 600 IN NSEC3 1 0 10 - VA2VG5BEMCKQP6MS5NHHGL18031BIA7M NS SOA RRSIG DNSKEY NSEC3PARAM
+ 600 RRSIG NSEC3 7 2 600 20110914225156 (
+ 20110815225156 39833 test.
+ rahhkfiF+Rk6oqbWTdu9qcwhmj5hbDuIFdiJ
+ GmaG+cFSv5Mjp+txNVCvBK9Hq/VpW0ypen/3
+ JC0sVAugSX+HAKAgyaMKmgWCvoQZ6ZSJUh7o
+ LRPcT+oxVXQAqjovxpaV8k6sYo44tpljPdOD
+ UluWAP5SrmJKjzCxs27KGRx8MK4= )
+VA2VG5BEMCKQP6MS5NHHGL18031BIA7M.test. 600 IN NSEC3 1 0 10 - VAKOQ2TPD7S25NFBJT73J3C4OGU10RJ5 TXT RRSIG
+ 600 RRSIG NSEC3 7 2 600 20110914225156 (
+ 20110815225156 39833 test.
+ XcBeZ8lo9Qo8z56+1FdGDjh6ZHCfO+MQ/wnY
+ TEUo/aWLkPTyq39nLhe0qVBJxmDpM+KQFuG9
+ cjQT5fvrlrY+lv6dedB64EBMYy4kKbIv7N5+
+ r6+sfWlvtKsfXxysLSk2+jLEm5NuLFrOdNas
+ WLVsq741D3YcWt4kM1HCyk3DNF8= )
+FA1T7MKUUV9SD4VDBJQ3GRFK1IDTCKL7.test. 600 IN NSEC3 1 0 10 - FBH6B0LHT9PPQB1P98D228HA1H52L8PO TXT RRSIG
+ 600 RRSIG NSEC3 7 2 600 20110914225156 (
+ 20110815225156 39833 test.
+ jB/vLrvx4sQQD7J3ZacAAyhcFmIPh7LH3ljw
+ IAIaeLb10oX5q1/nQKYdfq976TMy5sWpBcmd
+ i91WLxd+T/gOSumyP8bC3g+SUoyZ9wxY6A6a
+ MMx1rn0QA9IKrxMqojs9M3urJ8QAeIS+KyAn
+ rbyyJuG+EVm0prqlPZtzUi28WCI= )
+PQQ28M3U2MM08GGFV3JKR76G2H9IUJPC.test. 600 IN NSEC3 1 0 10 - U0UVS2SUP89P2TM3PJO4TC1GPJ2O6519
+ 600 RRSIG NSEC3 7 2 600 20110914225156 (
+ 20110815225156 39833 test.
+ asCOU9OkVWMvUU2IUpwMgdYf0faA04zPbaFf
+ qywYsv3NH01Lky6G3a0WUPAbBm7TAYx/ln8a
+ 559vlpp/gpXEl9CcLrjO6wy5i0ryp8gVHtKJ
+ rQlEc/uw4SY+S5t7FuZc2rNRdAbxVMYuwrvm
+ HBsKDPblre3e06ZZFEmnGFzCgmg= )
+VAKOQ2TPD7S25NFBJT73J3C4OGU10RJ5.test. 600 IN NSEC3 1 0 10 - VNCCJH8JPOLGLAGVMV3FKS09M7RRDU47 TXT RRSIG
+ 600 RRSIG NSEC3 7 2 600 20110914225156 (
+ 20110815225156 39833 test.
+ Pt4tKB1p/jsyLYab9LSt5MF1KTRT18nRTOox
+ q0IACkXkKx7W5xv6nSYXIB+nQzNp1Y1hhoXn
+ 9IFi0liPnIAOp73w4vybhfIdTFiEmHPHT6O9
+ VIx5cSriqBI6Qda8GtfeIb96P8SojbUk5BDI
+ g18iYjviGhQYRgpU3tg1qd7pbcc= )
+VNCCJH8JPOLGLAGVMV3FKS09M7RRDU47.test. 600 IN NSEC3 1 0 10 - 1F3JQ6EANHNHOCMUPQTVNM339VDTR51C
+ 600 RRSIG NSEC3 7 2 600 20110914225156 (
+ 20110815225156 39833 test.
+ ZMZPHawhkuzSV7C7zkgghH/jpw9CQVR1JUXq
+ pAeY2iIIWwNhfuskJaLgtu/5SuKnJtrv6D4N
+ g+lfEkBReia5xO/SCcHv8/hXEPH8vZ4xe1C9
+ 6GVB6ip2hKw2g5HpyF7X18WgwZ0cqPWVg+Q+
+ xRLpXH+53391Wt5rG7qJswn5RLE= )
diff --git a/lib/dns/tests/testdata/diff/zone1.data b/lib/dns/tests/testdata/diff/zone1.data
new file mode 100644
index 0000000..6eb87ab
--- /dev/null
+++ b/lib/dns/tests/testdata/diff/zone1.data
@@ -0,0 +1,13 @@
+; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+;
+; This Source Code Form is subject to the terms of the Mozilla Public
+; License, v. 2.0. If a copy of the MPL was not distributed with this
+; file, You can obtain one at http://mozilla.org/MPL/2.0/.
+;
+; See the COPYRIGHT file distributed with this work for additional
+; information regarding copyright ownership.
+
+@ 0 SOA . . 0 0 0 0 0
+@ 0 NS @
+@ 0 A 1.2.3.4
+remove 0 A 5.6.7.8
diff --git a/lib/dns/tests/testdata/diff/zone2.data b/lib/dns/tests/testdata/diff/zone2.data
new file mode 100644
index 0000000..d57d586
--- /dev/null
+++ b/lib/dns/tests/testdata/diff/zone2.data
@@ -0,0 +1,14 @@
+; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+;
+; This Source Code Form is subject to the terms of the Mozilla Public
+; License, v. 2.0. If a copy of the MPL was not distributed with this
+; file, You can obtain one at http://mozilla.org/MPL/2.0/.
+;
+; See the COPYRIGHT file distributed with this work for additional
+; information regarding copyright ownership.
+
+@ 0 SOA . . 0 0 0 0 0
+@ 0 NS @
+@ 0 A 1.2.3.4
+remove 0 A 5.6.7.8
+added 0 A 5.6.7.8
diff --git a/lib/dns/tests/testdata/diff/zone3.data b/lib/dns/tests/testdata/diff/zone3.data
new file mode 100644
index 0000000..65f12dd
--- /dev/null
+++ b/lib/dns/tests/testdata/diff/zone3.data
@@ -0,0 +1,12 @@
+; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+;
+; This Source Code Form is subject to the terms of the Mozilla Public
+; License, v. 2.0. If a copy of the MPL was not distributed with this
+; file, You can obtain one at http://mozilla.org/MPL/2.0/.
+;
+; See the COPYRIGHT file distributed with this work for additional
+; information regarding copyright ownership.
+
+@ 0 SOA . . 0 0 0 0 0
+@ 0 NS @
+@ 0 A 1.2.3.4
diff --git a/lib/dns/tests/testdata/dnstap/dnstap.saved b/lib/dns/tests/testdata/dnstap/dnstap.saved
new file mode 100644
index 0000000..c657f41
--- /dev/null
+++ b/lib/dns/tests/testdata/dnstap/dnstap.saved
Binary files differ
diff --git a/lib/dns/tests/testdata/dnstap/dnstap.text b/lib/dns/tests/testdata/dnstap/dnstap.text
new file mode 100644
index 0000000..71977e4
--- /dev/null
+++ b/lib/dns/tests/testdata/dnstap/dnstap.text
@@ -0,0 +1,96 @@
+03-Feb-2017 15:47:16.000 SQ 10.53.0.1:2112 -> 10.53.0.2:2112 UDP 40b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 SQ 10.53.0.1:2112 -> 10.53.0.2:2112 UDP 40b www.isc.org/IN/A
+03-Feb-2017 15:47:16.000 SQ 10.53.0.1:2112 -> 10.53.0.2:2112 UDP 40b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 SQ 10.53.0.1:2112 -> 10.53.0.2:2112 UDP 40b www.isc.org/IN/A
+03-Feb-2017 15:47:16.000 SQ 10.53.0.1:2112 -> 10.53.0.2:2112 TCP 40b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 SQ 10.53.0.1:2112 -> 10.53.0.2:2112 TCP 40b www.isc.org/IN/A
+03-Feb-2017 15:47:16.000 SQ 10.53.0.1:2112 -> 10.53.0.2:2112 TCP 40b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 SQ 10.53.0.1:2112 -> 10.53.0.2:2112 TCP 40b www.isc.org/IN/A
+03-Feb-2017 17:47:16.000 SR 10.53.0.1:2112 <- 10.53.0.2:2112 UDP 287b www.isc.org/IN/A
+03-Feb-2017 17:47:16.000 SR 10.53.0.1:2112 <- 10.53.0.2:2112 UDP 287b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 SR 10.53.0.1:2112 <- 10.53.0.2:2112 UDP 287b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 SR 10.53.0.1:2112 <- 10.53.0.2:2112 UDP 287b www.isc.org/IN/A
+03-Feb-2017 17:47:16.000 SR 10.53.0.1:2112 <- 10.53.0.2:2112 TCP 287b www.isc.org/IN/A
+03-Feb-2017 17:47:16.000 SR 10.53.0.1:2112 <- 10.53.0.2:2112 TCP 287b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 SR 10.53.0.1:2112 <- 10.53.0.2:2112 TCP 287b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 SR 10.53.0.1:2112 <- 10.53.0.2:2112 TCP 287b www.isc.org/IN/A
+03-Feb-2017 15:47:16.000 CQ 10.53.0.1:2112 -> 10.53.0.2:2112 UDP 40b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 CQ 10.53.0.1:2112 -> 10.53.0.2:2112 UDP 40b www.isc.org/IN/A
+03-Feb-2017 15:47:16.000 CQ 10.53.0.1:2112 -> 10.53.0.2:2112 UDP 40b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 CQ 10.53.0.1:2112 -> 10.53.0.2:2112 UDP 40b www.isc.org/IN/A
+03-Feb-2017 15:47:16.000 CQ 10.53.0.1:2112 -> 10.53.0.2:2112 TCP 40b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 CQ 10.53.0.1:2112 -> 10.53.0.2:2112 TCP 40b www.isc.org/IN/A
+03-Feb-2017 15:47:16.000 CQ 10.53.0.1:2112 -> 10.53.0.2:2112 TCP 40b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 CQ 10.53.0.1:2112 -> 10.53.0.2:2112 TCP 40b www.isc.org/IN/A
+03-Feb-2017 17:47:16.000 CR 10.53.0.1:2112 <- 10.53.0.2:2112 UDP 287b www.isc.org/IN/A
+03-Feb-2017 17:47:16.000 CR 10.53.0.1:2112 <- 10.53.0.2:2112 UDP 287b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 CR 10.53.0.1:2112 <- 10.53.0.2:2112 UDP 287b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 CR 10.53.0.1:2112 <- 10.53.0.2:2112 UDP 287b www.isc.org/IN/A
+03-Feb-2017 17:47:16.000 CR 10.53.0.1:2112 <- 10.53.0.2:2112 TCP 287b www.isc.org/IN/A
+03-Feb-2017 17:47:16.000 CR 10.53.0.1:2112 <- 10.53.0.2:2112 TCP 287b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 CR 10.53.0.1:2112 <- 10.53.0.2:2112 TCP 287b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 CR 10.53.0.1:2112 <- 10.53.0.2:2112 TCP 287b www.isc.org/IN/A
+03-Feb-2017 15:47:16.000 AQ 10.53.0.1:2112 -> 10.53.0.2:2112 UDP 40b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 AQ 10.53.0.1:2112 -> 10.53.0.2:2112 UDP 40b www.isc.org/IN/A
+03-Feb-2017 15:47:16.000 AQ 10.53.0.1:2112 -> 10.53.0.2:2112 UDP 40b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 AQ 10.53.0.1:2112 -> 10.53.0.2:2112 UDP 40b www.isc.org/IN/A
+03-Feb-2017 15:47:16.000 AQ 10.53.0.1:2112 -> 10.53.0.2:2112 TCP 40b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 AQ 10.53.0.1:2112 -> 10.53.0.2:2112 TCP 40b www.isc.org/IN/A
+03-Feb-2017 15:47:16.000 AQ 10.53.0.1:2112 -> 10.53.0.2:2112 TCP 40b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 AQ 10.53.0.1:2112 -> 10.53.0.2:2112 TCP 40b www.isc.org/IN/A
+03-Feb-2017 17:47:16.000 AR 10.53.0.1:2112 <- 10.53.0.2:2112 UDP 287b www.isc.org/IN/A
+03-Feb-2017 17:47:16.000 AR 10.53.0.1:2112 <- 10.53.0.2:2112 UDP 287b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 AR 10.53.0.1:2112 <- 10.53.0.2:2112 UDP 287b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 AR 10.53.0.1:2112 <- 10.53.0.2:2112 UDP 287b www.isc.org/IN/A
+03-Feb-2017 17:47:16.000 AR 10.53.0.1:2112 <- 10.53.0.2:2112 TCP 287b www.isc.org/IN/A
+03-Feb-2017 17:47:16.000 AR 10.53.0.1:2112 <- 10.53.0.2:2112 TCP 287b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 AR 10.53.0.1:2112 <- 10.53.0.2:2112 TCP 287b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 AR 10.53.0.1:2112 <- 10.53.0.2:2112 TCP 287b www.isc.org/IN/A
+03-Feb-2017 15:47:16.000 RQ 10.53.0.1:2112 -> 10.53.0.2:2112 UDP 40b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 RQ 10.53.0.1:2112 -> 10.53.0.2:2112 UDP 40b www.isc.org/IN/A
+03-Feb-2017 15:47:16.000 RQ 10.53.0.1:2112 -> 10.53.0.2:2112 UDP 40b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 RQ 10.53.0.1:2112 -> 10.53.0.2:2112 UDP 40b www.isc.org/IN/A
+03-Feb-2017 15:47:16.000 RQ 10.53.0.1:2112 -> 10.53.0.2:2112 TCP 40b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 RQ 10.53.0.1:2112 -> 10.53.0.2:2112 TCP 40b www.isc.org/IN/A
+03-Feb-2017 15:47:16.000 RQ 10.53.0.1:2112 -> 10.53.0.2:2112 TCP 40b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 RQ 10.53.0.1:2112 -> 10.53.0.2:2112 TCP 40b www.isc.org/IN/A
+03-Feb-2017 17:47:16.000 RR 10.53.0.1:2112 <- 10.53.0.2:2112 UDP 287b www.isc.org/IN/A
+03-Feb-2017 17:47:16.000 RR 10.53.0.1:2112 <- 10.53.0.2:2112 UDP 287b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 RR 10.53.0.1:2112 <- 10.53.0.2:2112 UDP 287b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 RR 10.53.0.1:2112 <- 10.53.0.2:2112 UDP 287b www.isc.org/IN/A
+03-Feb-2017 17:47:16.000 RR 10.53.0.1:2112 <- 10.53.0.2:2112 TCP 287b www.isc.org/IN/A
+03-Feb-2017 17:47:16.000 RR 10.53.0.1:2112 <- 10.53.0.2:2112 TCP 287b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 RR 10.53.0.1:2112 <- 10.53.0.2:2112 TCP 287b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 RR 10.53.0.1:2112 <- 10.53.0.2:2112 TCP 287b www.isc.org/IN/A
+03-Feb-2017 15:47:16.000 FQ 10.53.0.1:2112 -> 10.53.0.2:2112 UDP 40b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 FQ 10.53.0.1:2112 -> 10.53.0.2:2112 UDP 40b www.isc.org/IN/A
+03-Feb-2017 15:47:16.000 FQ 10.53.0.1:2112 -> 10.53.0.2:2112 UDP 40b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 FQ 10.53.0.1:2112 -> 10.53.0.2:2112 UDP 40b www.isc.org/IN/A
+03-Feb-2017 15:47:16.000 FQ 10.53.0.1:2112 -> 10.53.0.2:2112 TCP 40b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 FQ 10.53.0.1:2112 -> 10.53.0.2:2112 TCP 40b www.isc.org/IN/A
+03-Feb-2017 15:47:16.000 FQ 10.53.0.1:2112 -> 10.53.0.2:2112 TCP 40b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 FQ 10.53.0.1:2112 -> 10.53.0.2:2112 TCP 40b www.isc.org/IN/A
+03-Feb-2017 17:47:16.000 FR 10.53.0.1:2112 <- 10.53.0.2:2112 UDP 287b www.isc.org/IN/A
+03-Feb-2017 17:47:16.000 FR 10.53.0.1:2112 <- 10.53.0.2:2112 UDP 287b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 FR 10.53.0.1:2112 <- 10.53.0.2:2112 UDP 287b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 FR 10.53.0.1:2112 <- 10.53.0.2:2112 UDP 287b www.isc.org/IN/A
+03-Feb-2017 17:47:16.000 FR 10.53.0.1:2112 <- 10.53.0.2:2112 TCP 287b www.isc.org/IN/A
+03-Feb-2017 17:47:16.000 FR 10.53.0.1:2112 <- 10.53.0.2:2112 TCP 287b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 FR 10.53.0.1:2112 <- 10.53.0.2:2112 TCP 287b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 FR 10.53.0.1:2112 <- 10.53.0.2:2112 TCP 287b www.isc.org/IN/A
+03-Feb-2017 15:47:16.000 TQ 10.53.0.1:2112 -> 10.53.0.2:2112 UDP 40b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 TQ 10.53.0.1:2112 -> 10.53.0.2:2112 UDP 40b www.isc.org/IN/A
+03-Feb-2017 15:47:16.000 TQ 10.53.0.1:2112 -> 10.53.0.2:2112 UDP 40b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 TQ 10.53.0.1:2112 -> 10.53.0.2:2112 UDP 40b www.isc.org/IN/A
+03-Feb-2017 15:47:16.000 TQ 10.53.0.1:2112 -> 10.53.0.2:2112 TCP 40b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 TQ 10.53.0.1:2112 -> 10.53.0.2:2112 TCP 40b www.isc.org/IN/A
+03-Feb-2017 15:47:16.000 TQ 10.53.0.1:2112 -> 10.53.0.2:2112 TCP 40b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 TQ 10.53.0.1:2112 -> 10.53.0.2:2112 TCP 40b www.isc.org/IN/A
+03-Feb-2017 17:47:16.000 TR 10.53.0.1:2112 <- 10.53.0.2:2112 UDP 287b www.isc.org/IN/A
+03-Feb-2017 17:47:16.000 TR 10.53.0.1:2112 <- 10.53.0.2:2112 UDP 287b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 TR 10.53.0.1:2112 <- 10.53.0.2:2112 UDP 287b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 TR 10.53.0.1:2112 <- 10.53.0.2:2112 UDP 287b www.isc.org/IN/A
+03-Feb-2017 17:47:16.000 TR 10.53.0.1:2112 <- 10.53.0.2:2112 TCP 287b www.isc.org/IN/A
+03-Feb-2017 17:47:16.000 TR 10.53.0.1:2112 <- 10.53.0.2:2112 TCP 287b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 TR 10.53.0.1:2112 <- 10.53.0.2:2112 TCP 287b www.isc.org/IN/A
+03-Feb-2017 16:47:16.830 TR 10.53.0.1:2112 <- 10.53.0.2:2112 TCP 287b www.isc.org/IN/A
diff --git a/lib/dns/tests/testdata/dnstap/query.auth b/lib/dns/tests/testdata/dnstap/query.auth
new file mode 100644
index 0000000..a14f850
--- /dev/null
+++ b/lib/dns/tests/testdata/dnstap/query.auth
@@ -0,0 +1,4 @@
+# authoritative query, www.isc.org/A
+8d 24 00 20 00 01 00 00 00 00 00 01 03 77 77 77
+03 69 73 63 03 6f 72 67 00 00 01 00 01 00 00 29
+10 00 00 00 00 00 00 00
diff --git a/lib/dns/tests/testdata/dnstap/query.recursive b/lib/dns/tests/testdata/dnstap/query.recursive
new file mode 100644
index 0000000..8ee705f
--- /dev/null
+++ b/lib/dns/tests/testdata/dnstap/query.recursive
@@ -0,0 +1,4 @@
+# recursive query for www.isc.org/A
+bf 08 01 20 00 01 00 00 00 00 00 01 03 77 77 77
+03 69 73 63 03 6f 72 67 00 00 01 00 01 00 00 29
+10 00 00 00 00 00 00 00
diff --git a/lib/dns/tests/testdata/dnstap/response.auth b/lib/dns/tests/testdata/dnstap/response.auth
new file mode 100644
index 0000000..4d0ea81
--- /dev/null
+++ b/lib/dns/tests/testdata/dnstap/response.auth
@@ -0,0 +1,19 @@
+# authoritative response, www.isc.org/A
+8d 24 84 00 00 01 00 01 00 04 00 07 03 77 77 77
+03 69 73 63 03 6f 72 67 00 00 01 00 01 c0 0c 00
+01 00 01 00 00 00 3c 00 04 95 14 40 45 c0 10 00
+02 00 01 00 00 1c 20 00 0d 03 61 6d 73 06 73 6e
+73 2d 70 62 c0 10 c0 10 00 02 00 01 00 00 1c 20
+00 07 04 73 66 62 61 c0 3d c0 10 00 02 00 01 00
+00 1c 20 00 19 02 6e 73 03 69 73 63 0b 61 66 69
+6c 69 61 73 2d 6e 73 74 04 69 6e 66 6f 00 c0 10
+00 02 00 01 00 00 1c 20 00 06 03 6f 72 64 c0 3d
+c0 39 00 01 00 01 00 00 1c 20 00 04 c7 06 01 1e
+c0 39 00 1c 00 01 00 00 1c 20 00 10 20 01 05 00
+00 60 00 00 00 00 00 00 00 00 00 30 c0 8a 00 01
+00 01 00 00 1c 20 00 04 c7 06 00 1e c0 8a 00 1c
+00 01 00 00 1c 20 00 10 20 01 05 00 00 71 00 00
+00 00 00 00 00 00 00 30 c0 52 00 01 00 01 00 00
+1c 20 00 04 95 14 40 03 c0 52 00 1c 00 01 00 00
+1c 20 00 10 20 01 04 f8 00 00 00 02 00 00 00 00
+00 00 00 19 00 00 29 10 00 00 00 00 00 00 00
diff --git a/lib/dns/tests/testdata/dnstap/response.recursive b/lib/dns/tests/testdata/dnstap/response.recursive
new file mode 100644
index 0000000..6e3a3cf
--- /dev/null
+++ b/lib/dns/tests/testdata/dnstap/response.recursive
@@ -0,0 +1,19 @@
+# recursive response, www.isc.org/A
+bf 08 81 a0 00 01 00 01 00 04 00 07 03 77 77 77
+03 69 73 63 03 6f 72 67 00 00 01 00 01 c0 0c 00
+01 00 01 00 00 00 15 00 04 95 14 40 45 c0 10 00
+02 00 01 00 00 1b a6 00 0e 04 73 66 62 61 06 73
+6e 73 2d 70 62 c0 10 c0 10 00 02 00 01 00 00 1b
+a6 00 06 03 6f 72 64 c0 3e c0 10 00 02 00 01 00
+00 1b a6 00 19 02 6e 73 03 69 73 63 0b 61 66 69
+6c 69 61 73 2d 6e 73 74 04 69 6e 66 6f 00 c0 10
+00 02 00 01 00 00 1b a6 00 06 03 61 6d 73 c0 3e
+c0 8a 00 01 00 01 00 00 b1 d5 00 04 c7 06 01 1e
+c0 8a 00 1c 00 01 00 00 b1 d5 00 10 20 01 05 00
+00 60 00 00 00 00 00 00 00 00 00 30 c0 53 00 01
+00 01 00 00 b1 d5 00 04 c7 06 00 1e c0 53 00 1c
+00 01 00 00 b1 d5 00 10 20 01 05 00 00 71 00 00
+00 00 00 00 00 00 00 30 c0 39 00 01 00 01 00 00
+b1 d5 00 04 95 14 40 03 c0 39 00 1c 00 01 00 00
+b1 d5 00 10 20 01 04 f8 00 00 00 02 00 00 00 00
+00 00 00 19 00 00 29 10 00 00 00 00 00 00 00
diff --git a/lib/dns/tests/testdata/dst/Ktest.+001+00002.key b/lib/dns/tests/testdata/dst/Ktest.+001+00002.key
new file mode 100644
index 0000000..a8b4b4d
--- /dev/null
+++ b/lib/dns/tests/testdata/dst/Ktest.+001+00002.key
@@ -0,0 +1 @@
+test. IN DNSKEY 49152 2 1
diff --git a/lib/dns/tests/testdata/dst/Ktest.+001+54622.key b/lib/dns/tests/testdata/dst/Ktest.+001+54622.key
new file mode 100644
index 0000000..b0277e3
--- /dev/null
+++ b/lib/dns/tests/testdata/dst/Ktest.+001+54622.key
@@ -0,0 +1 @@
+test. IN DNSKEY 257 3 1 AQPQjwSpaVzxIgRCpiUoozUQKGh2oX8NIFKDOvtxK+tn536OZg2cROKTlgGEHXJK9YHfW/6nzQULTVpb63P+SQMmjCCidb8IYyhItixRztVeJQ==
diff --git a/lib/dns/tests/testdata/dst/Ktest.+001+54622.private b/lib/dns/tests/testdata/dst/Ktest.+001+54622.private
new file mode 100644
index 0000000..c97ac30
--- /dev/null
+++ b/lib/dns/tests/testdata/dst/Ktest.+001+54622.private
@@ -0,0 +1,10 @@
+Private-key-format: v1.2
+Algorithm: 1 (RSA)
+Modulus: 0I8EqWlc8SIEQqYlKKM1EChodqF/DSBSgzr7cSvrZ+d+jmYNnETik5YBhB1ySvWB31v+p80FC01aW+tz/kkDJowgonW/CGMoSLYsUc7VXiU=
+PublicExponent: Aw==
+PrivateExponent: iwoDG5uTS2wC1xluGxd4tXBFpGuqCMA3AidSS3Kc7++ptEQJEtiXC9kfCJMvZhGfQLaujft2OgrmkcuDVtPIbQWEENhyJhb4Lk82kFXbfus=
+Prime1: /rSKuzcZY7R5cY2YWD4CiBNyj9WJMq1wWmBnb9+5M08nTl5E9NW5qQ==
+Prime2: 0Z5shXQYd16E2Gs6e5WxtO0Oqlly2KkSqXohwTQWDWTb8Pw0WTZmHQ==
+Exponent1: qc2x0iS7l82mS7O65X6sWrehtTkGIcj1kZWaSpUmIjTE3umDTePRGw==
+Exponent2: i77zA6K6+j8DOvIm/Q52eJ4JxuZMkHC3G6bBK3gOs5iSoKgi5iREEw==
+Coefficient: 3+wYZB0SJad7z2EsjzgbSlg6CawoaOvrROGSbwSiW5DCsMFROudOTw==
diff --git a/lib/dns/tests/testdata/dst/Ktest.+003+23616.key b/lib/dns/tests/testdata/dst/Ktest.+003+23616.key
new file mode 100644
index 0000000..958d585
--- /dev/null
+++ b/lib/dns/tests/testdata/dst/Ktest.+003+23616.key
@@ -0,0 +1 @@
+test. IN DNSKEY 16641 3 3 ANp1//lqDlEfTavcFI+cyudNfgEz73V/K7fSDvkA0eDYcGg/kSvEjAEO/oLWCERltkuC55ZcM/mSv17WF1d/wR6kww/pLI9eXwkjftAYqs5sNxk+mbEGl6zwve9wq5z7IoTY5/J4l7XLCKftg/wGvrzXQhggIkRvEh3myhxd+ouILcpfvTIthWlTKiH59tSJpmgmiSMTE7nDYaf10iVRWN6DMSprgejiH05/fpmyZAt44tyAh4m1wXS5u4tam1PXDJYJozn7EfQ8e2weIv1yC+t6PHSx
diff --git a/lib/dns/tests/testdata/dst/Ktest.+003+23616.private b/lib/dns/tests/testdata/dst/Ktest.+003+23616.private
new file mode 100644
index 0000000..5781c9d
--- /dev/null
+++ b/lib/dns/tests/testdata/dst/Ktest.+003+23616.private
@@ -0,0 +1,7 @@
+Private-key-format: v1.2
+Algorithm: 3 (DSA)
+Prime(p): 73V/K7fSDvkA0eDYcGg/kSvEjAEO/oLWCERltkuC55ZcM/mSv17WF1d/wR6kww/pLI9eXwkjftAYqs5sNxk+mQ==
+Subprime(q): 2nX/+WoOUR9Nq9wUj5zK501+ATM=
+Base(g): sQaXrPC973CrnPsihNjn8niXtcsIp+2D/Aa+vNdCGCAiRG8SHebKHF36i4gtyl+9Mi2FaVMqIfn21ImmaCaJIw==
+Private_value(x): Nky4tvIwg6xlcyeHXr4k2DEZg0E=
+Public_value(y): ExO5w2Gn9dIlUVjegzEqa4Ho4h9Of36ZsmQLeOLcgIeJtcF0ubuLWptT1wyWCaM5+xH0PHtsHiL9cgvrejx0sQ==
diff --git a/lib/dns/tests/testdata/dst/Ktest.+003+49667.key b/lib/dns/tests/testdata/dst/Ktest.+003+49667.key
new file mode 100644
index 0000000..fb73f57
--- /dev/null
+++ b/lib/dns/tests/testdata/dst/Ktest.+003+49667.key
@@ -0,0 +1 @@
+test. IN DNSKEY 49152 2 3
diff --git a/lib/dns/tests/testdata/dst/test1.data b/lib/dns/tests/testdata/dst/test1.data
new file mode 100644
index 0000000..b1a9bf5
--- /dev/null
+++ b/lib/dns/tests/testdata/dst/test1.data
@@ -0,0 +1,3077 @@
+Network Working Group P. Mockapetris
+Request for Comments: 1035 ISI
+ November 1987
+Obsoletes: RFCs 882, 883, 973
+
+ DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION
+
+
+1. STATUS OF THIS MEMO
+
+This RFC describes the details of the domain system and protocol, and
+assumes that the reader is familiar with the concepts discussed in a
+companion RFC, "Domain Names - Concepts and Facilities" [RFC-1034].
+
+The domain system is a mixture of functions and data types which are an
+official protocol and functions and data types which are still
+experimental. Since the domain system is intentionally extensible, new
+data types and experimental behavior should always be expected in parts
+of the system beyond the official protocol. The official protocol parts
+include standard queries, responses and the Internet class RR data
+formats (e.g., host addresses). Since the previous RFC set, several
+definitions have changed, so some previous definitions are obsolete.
+
+Experimental or obsolete features are clearly marked in these RFCs, and
+such information should be used with caution.
+
+The reader is especially cautioned not to depend on the values which
+appear in examples to be current or complete, since their purpose is
+primarily pedagogical. Distribution of this memo is unlimited.
+
+ Table of Contents
+
+ 1. STATUS OF THIS MEMO 1
+ 2. INTRODUCTION 3
+ 2.1. Overview 3
+ 2.2. Common configurations 4
+ 2.3. Conventions 7
+ 2.3.1. Preferred name syntax 7
+ 2.3.2. Data Transmission Order 8
+ 2.3.3. Character Case 9
+ 2.3.4. Size limits 10
+ 3. DOMAIN NAME SPACE AND RR DEFINITIONS 10
+ 3.1. Name space definitions 10
+ 3.2. RR definitions 11
+ 3.2.1. Format 11
+ 3.2.2. TYPE values 12
+ 3.2.3. QTYPE values 12
+ 3.2.4. CLASS values 13
+
+
+
+Mockapetris [Page 1]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+ 3.2.5. QCLASS values 13
+ 3.3. Standard RRs 13
+ 3.3.1. CNAME RDATA format 14
+ 3.3.2. HINFO RDATA format 14
+ 3.3.3. MB RDATA format (EXPERIMENTAL) 14
+ 3.3.4. MD RDATA format (Obsolete) 15
+ 3.3.5. MF RDATA format (Obsolete) 15
+ 3.3.6. MG RDATA format (EXPERIMENTAL) 16
+ 3.3.7. MINFO RDATA format (EXPERIMENTAL) 16
+ 3.3.8. MR RDATA format (EXPERIMENTAL) 17
+ 3.3.9. MX RDATA format 17
+ 3.3.10. NULL RDATA format (EXPERIMENTAL) 17
+ 3.3.11. NS RDATA format 18
+ 3.3.12. PTR RDATA format 18
+ 3.3.13. SOA RDATA format 19
+ 3.3.14. TXT RDATA format 20
+ 3.4. ARPA Internet specific RRs 20
+ 3.4.1. A RDATA format 20
+ 3.4.2. WKS RDATA format 21
+ 3.5. IN-ADDR.ARPA domain 22
+ 3.6. Defining new types, classes, and special namespaces 24
+ 4. MESSAGES 25
+ 4.1. Format 25
+ 4.1.1. Header section format 26
+ 4.1.2. Question section format 28
+ 4.1.3. Resource record format 29
+ 4.1.4. Message compression 30
+ 4.2. Transport 32
+ 4.2.1. UDP usage 32
+ 4.2.2. TCP usage 32
+ 5. MASTER FILES 33
+ 5.1. Format 33
+ 5.2. Use of master files to define zones 35
+ 5.3. Master file example 36
+ 6. NAME SERVER IMPLEMENTATION 37
+ 6.1. Architecture 37
+ 6.1.1. Control 37
+ 6.1.2. Database 37
+ 6.1.3. Time 39
+ 6.2. Standard query processing 39
+ 6.3. Zone refresh and reload processing 39
+ 6.4. Inverse queries (Optional) 40
+ 6.4.1. The contents of inverse queries and responses 40
+ 6.4.2. Inverse query and response example 41
+ 6.4.3. Inverse query processing 42
+
+
+
+
+
+
+Mockapetris [Page 2]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+ 6.5. Completion queries and responses 42
+ 7. RESOLVER IMPLEMENTATION 43
+ 7.1. Transforming a user request into a query 43
+ 7.2. Sending the queries 44
+ 7.3. Processing responses 46
+ 7.4. Using the cache 47
+ 8. MAIL SUPPORT 47
+ 8.1. Mail exchange binding 48
+ 8.2. Mailbox binding (Experimental) 48
+ 9. REFERENCES and BIBLIOGRAPHY 50
+ Index 54
+
+2. INTRODUCTION
+
+2.1. Overview
+
+The goal of domain names is to provide a mechanism for naming resources
+in such a way that the names are usable in different hosts, networks,
+protocol families, internets, and administrative organizations.
+
+From the user's point of view, domain names are useful as arguments to a
+local agent, called a resolver, which retrieves information associated
+with the domain name. Thus a user might ask for the host address or
+mail information associated with a particular domain name. To enable
+the user to request a particular type of information, an appropriate
+query type is passed to the resolver with the domain name. To the user,
+the domain tree is a single information space; the resolver is
+responsible for hiding the distribution of data among name servers from
+the user.
+
+From the resolver's point of view, the database that makes up the domain
+space is distributed among various name servers. Different parts of the
+domain space are stored in different name servers, although a particular
+data item will be stored redundantly in two or more name servers. The
+resolver starts with knowledge of at least one name server. When the
+resolver processes a user query it asks a known name server for the
+information; in return, the resolver either receives the desired
+information or a referral to another name server. Using these
+referrals, resolvers learn the identities and contents of other name
+servers. Resolvers are responsible for dealing with the distribution of
+the domain space and dealing with the effects of name server failure by
+consulting redundant databases in other servers.
+
+Name servers manage two kinds of data. The first kind of data held in
+sets called zones; each zone is the complete database for a particular
+"pruned" subtree of the domain space. This data is called
+authoritative. A name server periodically checks to make sure that its
+zones are up to date, and if not, obtains a new copy of updated zones
+
+
+
+Mockapetris [Page 3]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+from master files stored locally or in another name server. The second
+kind of data is cached data which was acquired by a local resolver.
+This data may be incomplete, but improves the performance of the
+retrieval process when non-local data is repeatedly accessed. Cached
+data is eventually discarded by a timeout mechanism.
+
+This functional structure isolates the problems of user interface,
+failure recovery, and distribution in the resolvers and isolates the
+database update and refresh problems in the name servers.
+
+2.2. Common configurations
+
+A host can participate in the domain name system in a number of ways,
+depending on whether the host runs programs that retrieve information
+from the domain system, name servers that answer queries from other
+hosts, or various combinations of both functions. The simplest, and
+perhaps most typical, configuration is shown below:
+
+ Local Host | Foreign
+ |
+ +---------+ +----------+ | +--------+
+ | | user queries | |queries | | |
+ | User |-------------->| |---------|->|Foreign |
+ | Program | | Resolver | | | Name |
+ | |<--------------| |<--------|--| Server |
+ | | user responses| |responses| | |
+ +---------+ +----------+ | +--------+
+ | A |
+ cache additions | | references |
+ V | |
+ +----------+ |
+ | cache | |
+ +----------+ |
+
+User programs interact with the domain name space through resolvers; the
+format of user queries and user responses is specific to the host and
+its operating system. User queries will typically be operating system
+calls, and the resolver and its cache will be part of the host operating
+system. Less capable hosts may choose to implement the resolver as a
+subroutine to be linked in with every program that needs its services.
+Resolvers answer user queries with information they acquire via queries
+to foreign name servers and the local cache.
+
+Note that the resolver may have to make several queries to several
+different foreign name servers to answer a particular user query, and
+hence the resolution of a user query may involve several network
+accesses and an arbitrary amount of time. The queries to foreign name
+servers and the corresponding responses have a standard format described
+
+
+
+Mockapetris [Page 4]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+in this memo, and may be datagrams.
+
+Depending on its capabilities, a name server could be a stand alone
+program on a dedicated machine or a process or processes on a large
+timeshared host. A simple configuration might be:
+
+ Local Host | Foreign
+ |
+ +---------+ |
+ / /| |
+ +---------+ | +----------+ | +--------+
+ | | | | |responses| | |
+ | | | | Name |---------|->|Foreign |
+ | Master |-------------->| Server | | |Resolver|
+ | files | | | |<--------|--| |
+ | |/ | | queries | +--------+
+ +---------+ +----------+ |
+
+Here a primary name server acquires information about one or more zones
+by reading master files from its local file system, and answers queries
+about those zones that arrive from foreign resolvers.
+
+The DNS requires that all zones be redundantly supported by more than
+one name server. Designated secondary servers can acquire zones and
+check for updates from the primary server using the zone transfer
+protocol of the DNS. This configuration is shown below:
+
+ Local Host | Foreign
+ |
+ +---------+ |
+ / /| |
+ +---------+ | +----------+ | +--------+
+ | | | | |responses| | |
+ | | | | Name |---------|->|Foreign |
+ | Master |-------------->| Server | | |Resolver|
+ | files | | | |<--------|--| |
+ | |/ | | queries | +--------+
+ +---------+ +----------+ |
+ A |maintenance | +--------+
+ | +------------|->| |
+ | queries | |Foreign |
+ | | | Name |
+ +------------------|--| Server |
+ maintenance responses | +--------+
+
+In this configuration, the name server periodically establishes a
+virtual circuit to a foreign name server to acquire a copy of a zone or
+to check that an existing copy has not changed. The messages sent for
+
+
+
+Mockapetris [Page 5]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+these maintenance activities follow the same form as queries and
+responses, but the message sequences are somewhat different.
+
+The information flow in a host that supports all aspects of the domain
+name system is shown below:
+
+ Local Host | Foreign
+ |
+ +---------+ +----------+ | +--------+
+ | | user queries | |queries | | |
+ | User |-------------->| |---------|->|Foreign |
+ | Program | | Resolver | | | Name |
+ | |<--------------| |<--------|--| Server |
+ | | user responses| |responses| | |
+ +---------+ +----------+ | +--------+
+ | A |
+ cache additions | | references |
+ V | |
+ +----------+ |
+ | Shared | |
+ | database | |
+ +----------+ |
+ A | |
+ +---------+ refreshes | | references |
+ / /| | V |
+ +---------+ | +----------+ | +--------+
+ | | | | |responses| | |
+ | | | | Name |---------|->|Foreign |
+ | Master |-------------->| Server | | |Resolver|
+ | files | | | |<--------|--| |
+ | |/ | | queries | +--------+
+ +---------+ +----------+ |
+ A |maintenance | +--------+
+ | +------------|->| |
+ | queries | |Foreign |
+ | | | Name |
+ +------------------|--| Server |
+ maintenance responses | +--------+
+
+The shared database holds domain space data for the local name server
+and resolver. The contents of the shared database will typically be a
+mixture of authoritative data maintained by the periodic refresh
+operations of the name server and cached data from previous resolver
+requests. The structure of the domain data and the necessity for
+synchronization between name servers and resolvers imply the general
+characteristics of this database, but the actual format is up to the
+local implementor.
+
+
+
+
+Mockapetris [Page 6]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+Information flow can also be tailored so that a group of hosts act
+together to optimize activities. Sometimes this is done to offload less
+capable hosts so that they do not have to implement a full resolver.
+This can be appropriate for PCs or hosts which want to minimize the
+amount of new network code which is required. This scheme can also
+allow a group of hosts can share a small number of caches rather than
+maintaining a large number of separate caches, on the premise that the
+centralized caches will have a higher hit ratio. In either case,
+resolvers are replaced with stub resolvers which act as front ends to
+resolvers located in a recursive server in one or more name servers
+known to perform that service:
+
+ Local Hosts | Foreign
+ |
+ +---------+ |
+ | | responses |
+ | Stub |<--------------------+ |
+ | Resolver| | |
+ | |----------------+ | |
+ +---------+ recursive | | |
+ queries | | |
+ V | |
+ +---------+ recursive +----------+ | +--------+
+ | | queries | |queries | | |
+ | Stub |-------------->| Recursive|---------|->|Foreign |
+ | Resolver| | Server | | | Name |
+ | |<--------------| |<--------|--| Server |
+ +---------+ responses | |responses| | |
+ +----------+ | +--------+
+ | Central | |
+ | cache | |
+ +----------+ |
+
+In any case, note that domain components are always replicated for
+reliability whenever possible.
+
+2.3. Conventions
+
+The domain system has several conventions dealing with low-level, but
+fundamental, issues. While the implementor is free to violate these
+conventions WITHIN HIS OWN SYSTEM, he must observe these conventions in
+ALL behavior observed from other hosts.
+
+2.3.1. Preferred name syntax
+
+The DNS specifications attempt to be as general as possible in the rules
+for constructing domain names. The idea is that the name of any
+existing object can be expressed as a domain name with minimal changes.
+
+
+
+Mockapetris [Page 7]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+However, when assigning a domain name for an object, the prudent user
+will select a name which satisfies both the rules of the domain system
+and any existing rules for the object, whether these rules are published
+or implied by existing programs.
+
+For example, when naming a mail domain, the user should satisfy both the
+rules of this memo and those in RFC-822. When creating a new host name,
+the old rules for HOSTS.TXT should be followed. This avoids problems
+when old software is converted to use domain names.
+
+The following syntax will result in fewer problems with many
+
+applications that use domain names (e.g., mail, TELNET).
+
+<domain> ::= <subdomain> | " "
+
+<subdomain> ::= <label> | <subdomain> "." <label>
+
+<label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
+
+<ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
+
+<let-dig-hyp> ::= <let-dig> | "-"
+
+<let-dig> ::= <letter> | <digit>
+
+<letter> ::= any one of the 52 alphabetic characters A through Z in
+upper case and a through z in lower case
+
+<digit> ::= any one of the ten digits 0 through 9
+
+Note that while upper and lower case letters are allowed in domain
+names, no significance is attached to the case. That is, two names with
+the same spelling but different case are to be treated as if identical.
+
+The labels must follow the rules for ARPANET host names. They must
+start with a letter, end with a letter or digit, and have as interior
+characters only letters, digits, and hyphen. There are also some
+restrictions on the length. Labels must be 63 characters or less.
+
+For example, the following strings identify hosts in the Internet:
+
+A.ISI.EDU XX.LCS.MIT.EDU SRI-NIC.ARPA
+
+2.3.2. Data Transmission Order
+
+The order of transmission of the header and data described in this
+document is resolved to the octet level. Whenever a diagram shows a
+
+
+
+Mockapetris [Page 8]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+group of octets, the order of transmission of those octets is the normal
+order in which they are read in English. For example, in the following
+diagram, the octets are transmitted in the order they are numbered.
+
+ 0 1
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | 1 | 2 |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | 3 | 4 |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | 5 | 6 |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+Whenever an octet represents a numeric quantity, the left most bit in
+the diagram is the high order or most significant bit. That is, the bit
+labeled 0 is the most significant bit. For example, the following
+diagram represents the value 170 (decimal).
+
+ 0 1 2 3 4 5 6 7
+ +-+-+-+-+-+-+-+-+
+ |1 0 1 0 1 0 1 0|
+ +-+-+-+-+-+-+-+-+
+
+Similarly, whenever a multi-octet field represents a numeric quantity
+the left most bit of the whole field is the most significant bit. When
+a multi-octet quantity is transmitted the most significant octet is
+transmitted first.
+
+2.3.3. Character Case
+
+For all parts of the DNS that are part of the official protocol, all
+comparisons between character strings (e.g., labels, domain names, etc.)
+are done in a case-insensitive manner. At present, this rule is in
+force throughout the domain system without exception. However, future
+additions beyond current usage may need to use the full binary octet
+capabilities in names, so attempts to store domain names in 7-bit ASCII
+or use of special bytes to terminate labels, etc., should be avoided.
+
+When data enters the domain system, its original case should be
+preserved whenever possible. In certain circumstances this cannot be
+done. For example, if two RRs are stored in a database, one at x.y and
+one at X.Y, they are actually stored at the same place in the database,
+and hence only one casing would be preserved. The basic rule is that
+case can be discarded only when data is used to define structure in a
+database, and two names are identical when compared in a case
+insensitive manner.
+
+
+
+
+Mockapetris [Page 9]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+Loss of case sensitive data must be minimized. Thus while data for x.y
+and X.Y may both be stored under a single location x.y or X.Y, data for
+a.x and B.X would never be stored under A.x, A.X, b.x, or b.X. In
+general, this preserves the case of the first label of a domain name,
+but forces standardization of interior node labels.
+
+Systems administrators who enter data into the domain database should
+take care to represent the data they supply to the domain system in a
+case-consistent manner if their system is case-sensitive. The data
+distribution system in the domain system will ensure that consistent
+representations are preserved.
+
+2.3.4. Size limits
+
+Various objects and parameters in the DNS have size limits. They are
+listed below. Some could be easily changed, others are more
+fundamental.
+
+labels 63 octets or less
+
+names 255 octets or less
+
+TTL positive values of a signed 32 bit number.
+
+UDP messages 512 octets or less
+
+3. DOMAIN NAME SPACE AND RR DEFINITIONS
+
+3.1. Name space definitions
+
+Domain names in messages are expressed in terms of a sequence of labels.
+Each label is represented as a one octet length field followed by that
+number of octets. Since every domain name ends with the null label of
+the root, a domain name is terminated by a length byte of zero. The
+high order two bits of every length octet must be zero, and the
+remaining six bits of the length field limit the label to 63 octets or
+less.
+
+To simplify implementations, the total length of a domain name (i.e.,
+label octets and label length octets) is restricted to 255 octets or
+less.
+
+Although labels can contain any 8 bit values in octets that make up a
+label, it is strongly recommended that labels follow the preferred
+syntax described elsewhere in this memo, which is compatible with
+existing host naming conventions. Name servers and resolvers must
+compare labels in a case-insensitive manner (i.e., A=a), assuming ASCII
+with zero parity. Non-alphabetic codes must match exactly.
+
+
+
+Mockapetris [Page 10]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+3.2. RR definitions
+
+3.2.1. Format
+
+All RRs have the same top level format shown below:
+
+ 1 1 1 1 1 1
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | |
+ / /
+ / NAME /
+ | |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | TYPE |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | CLASS |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | TTL |
+ | |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | RDLENGTH |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
+ / RDATA /
+ / /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+
+where:
+
+NAME an owner name, i.e., the name of the node to which this
+ resource record pertains.
+
+TYPE two octets containing one of the RR TYPE codes.
+
+CLASS two octets containing one of the RR CLASS codes.
+
+TTL a 32 bit signed integer that specifies the time interval
+ that the resource record may be cached before the source
+ of the information should again be consulted. Zero
+ values are interpreted to mean that the RR can only be
+ used for the transaction in progress, and should not be
+ cached. For example, SOA records are always distributed
+ with a zero TTL to prohibit caching. Zero values can
+ also be used for extremely volatile data.
+
+RDLENGTH an unsigned 16 bit integer that specifies the length in
+ octets of the RDATA field.
+
+
+
+Mockapetris [Page 11]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+RDATA a variable length string of octets that describes the
+ resource. The format of this information varies
+ according to the TYPE and CLASS of the resource record.
+
+3.2.2. TYPE values
+
+TYPE fields are used in resource records. Note that these types are a
+subset of QTYPEs.
+
+TYPE value and meaning
+
+A 1 a host address
+
+NS 2 an authoritative name server
+
+MD 3 a mail destination (Obsolete - use MX)
+
+MF 4 a mail forwarder (Obsolete - use MX)
+
+CNAME 5 the canonical name for an alias
+
+SOA 6 marks the start of a zone of authority
+
+MB 7 a mailbox domain name (EXPERIMENTAL)
+
+MG 8 a mail group member (EXPERIMENTAL)
+
+MR 9 a mail rename domain name (EXPERIMENTAL)
+
+NULL 10 a null RR (EXPERIMENTAL)
+
+WKS 11 a well known service description
+
+PTR 12 a domain name pointer
+
+HINFO 13 host information
+
+MINFO 14 mailbox or mail list information
+
+MX 15 mail exchange
+
+TXT 16 text strings
+
+3.2.3. QTYPE values
+
+QTYPE fields appear in the question part of a query. QTYPES are a
+superset of TYPEs, hence all TYPEs are valid QTYPEs. In addition, the
+following QTYPEs are defined:
+
+
+
+Mockapetris [Page 12]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+AXFR 252 A request for a transfer of an entire zone
+
+MAILB 253 A request for mailbox-related records (MB, MG or MR)
+
+MAILA 254 A request for mail agent RRs (Obsolete - see MX)
+
+* 255 A request for all records
+
+3.2.4. CLASS values
+
+CLASS fields appear in resource records. The following CLASS mnemonics
+and values are defined:
+
+IN 1 the Internet
+
+CS 2 the CSNET class (Obsolete - used only for examples in
+ some obsolete RFCs)
+
+CH 3 the CHAOS class
+
+HS 4 Hesiod [Dyer 87]
+
+3.2.5. QCLASS values
+
+QCLASS fields appear in the question section of a query. QCLASS values
+are a superset of CLASS values; every CLASS is a valid QCLASS. In
+addition to CLASS values, the following QCLASSes are defined:
+
+* 255 any class
+
+3.3. Standard RRs
+
+The following RR definitions are expected to occur, at least
+potentially, in all classes. In particular, NS, SOA, CNAME, and PTR
+will be used in all classes, and have the same format in all classes.
+Because their RDATA format is known, all domain names in the RDATA
+section of these RRs may be compressed.
+
+<domain-name> is a domain name represented as a series of labels, and
+terminated by a label with zero length. <character-string> is a single
+length octet followed by that number of characters. <character-string>
+is treated as binary information, and can be up to 256 characters in
+length (including the length octet).
+
+
+
+
+
+
+
+
+Mockapetris [Page 13]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+3.3.1. CNAME RDATA format
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ / CNAME /
+ / /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+CNAME A <domain-name> which specifies the canonical or primary
+ name for the owner. The owner name is an alias.
+
+CNAME RRs cause no additional section processing, but name servers may
+choose to restart the query at the canonical name in certain cases. See
+the description of name server logic in [RFC-1034] for details.
+
+3.3.2. HINFO RDATA format
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ / CPU /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ / OS /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+CPU A <character-string> which specifies the CPU type.
+
+OS A <character-string> which specifies the operating
+ system type.
+
+Standard values for CPU and OS can be found in [RFC-1010].
+
+HINFO records are used to acquire general information about a host. The
+main use is for protocols such as FTP that can use special procedures
+when talking between machines or operating systems of the same type.
+
+3.3.3. MB RDATA format (EXPERIMENTAL)
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ / MADNAME /
+ / /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+MADNAME A <domain-name> which specifies a host which has the
+ specified mailbox.
+
+
+
+Mockapetris [Page 14]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+MB records cause additional section processing which looks up an A type
+RRs corresponding to MADNAME.
+
+3.3.4. MD RDATA format (Obsolete)
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ / MADNAME /
+ / /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+MADNAME A <domain-name> which specifies a host which has a mail
+ agent for the domain which should be able to deliver
+ mail for the domain.
+
+MD records cause additional section processing which looks up an A type
+record corresponding to MADNAME.
+
+MD is obsolete. See the definition of MX and [RFC-974] for details of
+the new scheme. The recommended policy for dealing with MD RRs found in
+a master file is to reject them, or to convert them to MX RRs with a
+preference of 0.
+
+3.3.5. MF RDATA format (Obsolete)
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ / MADNAME /
+ / /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+MADNAME A <domain-name> which specifies a host which has a mail
+ agent for the domain which will accept mail for
+ forwarding to the domain.
+
+MF records cause additional section processing which looks up an A type
+record corresponding to MADNAME.
+
+MF is obsolete. See the definition of MX and [RFC-974] for details ofw
+the new scheme. The recommended policy for dealing with MD RRs found in
+a master file is to reject them, or to convert them to MX RRs with a
+preference of 10.
+
+
+
+
+
+
+
+Mockapetris [Page 15]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+3.3.6. MG RDATA format (EXPERIMENTAL)
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ / MGMNAME /
+ / /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+MGMNAME A <domain-name> which specifies a mailbox which is a
+ member of the mail group specified by the domain name.
+
+MG records cause no additional section processing.
+
+3.3.7. MINFO RDATA format (EXPERIMENTAL)
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ / RMAILBX /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ / EMAILBX /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+RMAILBX A <domain-name> which specifies a mailbox which is
+ responsible for the mailing list or mailbox. If this
+ domain name names the root, the owner of the MINFO RR is
+ responsible for itself. Note that many existing mailing
+ lists use a mailbox X-request for the RMAILBX field of
+ mailing list X, e.g., Msgroup-request for Msgroup. This
+ field provides a more general mechanism.
+
+
+EMAILBX A <domain-name> which specifies a mailbox which is to
+ receive error messages related to the mailing list or
+ mailbox specified by the owner of the MINFO RR (similar
+ to the ERRORS-TO: field which has been proposed). If
+ this domain name names the root, errors should be
+ returned to the sender of the message.
+
+MINFO records cause no additional section processing. Although these
+records can be associated with a simple mailbox, they are usually used
+with a mailing list.
+
+
+
+
+
+
+
+
+Mockapetris [Page 16]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+3.3.8. MR RDATA format (EXPERIMENTAL)
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ / NEWNAME /
+ / /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+NEWNAME A <domain-name> which specifies a mailbox which is the
+ proper rename of the specified mailbox.
+
+MR records cause no additional section processing. The main use for MR
+is as a forwarding entry for a user who has moved to a different
+mailbox.
+
+3.3.9. MX RDATA format
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | PREFERENCE |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ / EXCHANGE /
+ / /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+PREFERENCE A 16 bit integer which specifies the preference given to
+ this RR among others at the same owner. Lower values
+ are preferred.
+
+EXCHANGE A <domain-name> which specifies a host willing to act as
+ a mail exchange for the owner name.
+
+MX records cause type A additional section processing for the host
+specified by EXCHANGE. The use of MX RRs is explained in detail in
+[RFC-974].
+
+3.3.10. NULL RDATA format (EXPERIMENTAL)
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ / <anything> /
+ / /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+Anything at all may be in the RDATA field so long as it is 65535 octets
+or less.
+
+
+
+
+Mockapetris [Page 17]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+NULL records cause no additional section processing. NULL RRs are not
+allowed in master files. NULLs are used as placeholders in some
+experimental extensions of the DNS.
+
+3.3.11. NS RDATA format
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ / NSDNAME /
+ / /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+NSDNAME A <domain-name> which specifies a host which should be
+ authoritative for the specified class and domain.
+
+NS records cause both the usual additional section processing to locate
+a type A record, and, when used in a referral, a special search of the
+zone in which they reside for glue information.
+
+The NS RR states that the named host should be expected to have a zone
+starting at owner name of the specified class. Note that the class may
+not indicate the protocol family which should be used to communicate
+with the host, although it is typically a strong hint. For example,
+hosts which are name servers for either Internet (IN) or Hesiod (HS)
+class information are normally queried using IN class protocols.
+
+3.3.12. PTR RDATA format
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ / PTRDNAME /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+PTRDNAME A <domain-name> which points to some location in the
+ domain name space.
+
+PTR records cause no additional section processing. These RRs are used
+in special domains to point to some other location in the domain space.
+These records are simple data, and don't imply any special processing
+similar to that performed by CNAME, which identifies aliases. See the
+description of the IN-ADDR.ARPA domain for an example.
+
+
+
+
+
+
+
+
+Mockapetris [Page 18]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+3.3.13. SOA RDATA format
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ / MNAME /
+ / /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ / RNAME /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | SERIAL |
+ | |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | REFRESH |
+ | |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | RETRY |
+ | |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | EXPIRE |
+ | |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | MINIMUM |
+ | |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+MNAME The <domain-name> of the name server that was the
+ original or primary source of data for this zone.
+
+RNAME A <domain-name> which specifies the mailbox of the
+ person responsible for this zone.
+
+SERIAL The unsigned 32 bit version number of the original copy
+ of the zone. Zone transfers preserve this value. This
+ value wraps and should be compared using sequence space
+ arithmetic.
+
+REFRESH A 32 bit time interval before the zone should be
+ refreshed.
+
+RETRY A 32 bit time interval that should elapse before a
+ failed refresh should be retried.
+
+EXPIRE A 32 bit time value that specifies the upper limit on
+ the time interval that can elapse before the zone is no
+ longer authoritative.
+
+
+
+
+
+Mockapetris [Page 19]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+MINIMUM The unsigned 32 bit minimum TTL field that should be
+ exported with any RR from this zone.
+
+SOA records cause no additional section processing.
+
+All times are in units of seconds.
+
+Most of these fields are pertinent only for name server maintenance
+operations. However, MINIMUM is used in all query operations that
+retrieve RRs from a zone. Whenever a RR is sent in a response to a
+query, the TTL field is set to the maximum of the TTL field from the RR
+and the MINIMUM field in the appropriate SOA. Thus MINIMUM is a lower
+bound on the TTL field for all RRs in a zone. Note that this use of
+MINIMUM should occur when the RRs are copied into the response and not
+when the zone is loaded from a master file or via a zone transfer. The
+reason for this provison is to allow future dynamic update facilities to
+change the SOA RR with known semantics.
+
+
+3.3.14. TXT RDATA format
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ / TXT-DATA /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+TXT-DATA One or more <character-string>s.
+
+TXT RRs are used to hold descriptive text. The semantics of the text
+depends on the domain where it is found.
+
+3.4. Internet specific RRs
+
+3.4.1. A RDATA format
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | ADDRESS |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+ADDRESS A 32 bit Internet address.
+
+Hosts that have multiple Internet addresses will have multiple A
+records.
+
+
+
+
+
+Mockapetris [Page 20]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+A records cause no additional section processing. The RDATA section of
+an A line in a master file is an Internet address expressed as four
+decimal numbers separated by dots without any imbedded spaces (e.g.,
+"10.2.0.52" or "192.0.5.6").
+
+3.4.2. WKS RDATA format
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | ADDRESS |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | PROTOCOL | |
+ +--+--+--+--+--+--+--+--+ |
+ | |
+ / <BIT MAP> /
+ / /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+ADDRESS An 32 bit Internet address
+
+PROTOCOL An 8 bit IP protocol number
+
+<BIT MAP> A variable length bit map. The bit map must be a
+ multiple of 8 bits long.
+
+The WKS record is used to describe the well known services supported by
+a particular protocol on a particular internet address. The PROTOCOL
+field specifies an IP protocol number, and the bit map has one bit per
+port of the specified protocol. The first bit corresponds to port 0,
+the second to port 1, etc. If the bit map does not include a bit for a
+protocol of interest, that bit is assumed zero. The appropriate values
+and mnemonics for ports and protocols are specified in [RFC-1010].
+
+For example, if PROTOCOL=TCP (6), the 26th bit corresponds to TCP port
+25 (SMTP). If this bit is set, a SMTP server should be listening on TCP
+port 25; if zero, SMTP service is not supported on the specified
+address.
+
+The purpose of WKS RRs is to provide availability information for
+servers for TCP and UDP. If a server supports both TCP and UDP, or has
+multiple Internet addresses, then multiple WKS RRs are used.
+
+WKS RRs cause no additional section processing.
+
+In master files, both ports and protocols are expressed using mnemonics
+or decimal numbers.
+
+
+
+
+Mockapetris [Page 21]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+3.5. IN-ADDR.ARPA domain
+
+The Internet uses a special domain to support gateway location and
+Internet address to host mapping. Other classes may employ a similar
+strategy in other domains. The intent of this domain is to provide a
+guaranteed method to perform host address to host name mapping, and to
+facilitate queries to locate all gateways on a particular network in the
+Internet.
+
+Note that both of these services are similar to functions that could be
+performed by inverse queries; the difference is that this part of the
+domain name space is structured according to address, and hence can
+guarantee that the appropriate data can be located without an exhaustive
+search of the domain space.
+
+The domain begins at IN-ADDR.ARPA and has a substructure which follows
+the Internet addressing structure.
+
+Domain names in the IN-ADDR.ARPA domain are defined to have up to four
+labels in addition to the IN-ADDR.ARPA suffix. Each label represents
+one octet of an Internet address, and is expressed as a character string
+for a decimal value in the range 0-255 (with leading zeros omitted
+except in the case of a zero octet which is represented by a single
+zero).
+
+Host addresses are represented by domain names that have all four labels
+specified. Thus data for Internet address 10.2.0.52 is located at
+domain name 52.0.2.10.IN-ADDR.ARPA. The reversal, though awkward to
+read, allows zones to be delegated which are exactly one network of
+address space. For example, 10.IN-ADDR.ARPA can be a zone containing
+data for the ARPANET, while 26.IN-ADDR.ARPA can be a separate zone for
+MILNET. Address nodes are used to hold pointers to primary host names
+in the normal domain space.
+
+Network numbers correspond to some non-terminal nodes at various depths
+in the IN-ADDR.ARPA domain, since Internet network numbers are either 1,
+2, or 3 octets. Network nodes are used to hold pointers to the primary
+host names of gateways attached to that network. Since a gateway is, by
+definition, on more than one network, it will typically have two or more
+network nodes which point at it. Gateways will also have host level
+pointers at their fully qualified addresses.
+
+Both the gateway pointers at network nodes and the normal host pointers
+at full address nodes use the PTR RR to point back to the primary domain
+names of the corresponding hosts.
+
+For example, the IN-ADDR.ARPA domain will contain information about the
+ISI gateway between net 10 and 26, an MIT gateway from net 10 to MIT's
+
+
+
+Mockapetris [Page 22]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+net 18, and hosts A.ISI.EDU and MULTICS.MIT.EDU. Assuming that ISI
+gateway has addresses 10.2.0.22 and 26.0.0.103, and a name MILNET-
+GW.ISI.EDU, and the MIT gateway has addresses 10.0.0.77 and 18.10.0.4
+and a name GW.LCS.MIT.EDU, the domain database would contain:
+
+ 10.IN-ADDR.ARPA. PTR MILNET-GW.ISI.EDU.
+ 10.IN-ADDR.ARPA. PTR GW.LCS.MIT.EDU.
+ 18.IN-ADDR.ARPA. PTR GW.LCS.MIT.EDU.
+ 26.IN-ADDR.ARPA. PTR MILNET-GW.ISI.EDU.
+ 22.0.2.10.IN-ADDR.ARPA. PTR MILNET-GW.ISI.EDU.
+ 103.0.0.26.IN-ADDR.ARPA. PTR MILNET-GW.ISI.EDU.
+ 77.0.0.10.IN-ADDR.ARPA. PTR GW.LCS.MIT.EDU.
+ 4.0.10.18.IN-ADDR.ARPA. PTR GW.LCS.MIT.EDU.
+ 103.0.3.26.IN-ADDR.ARPA. PTR A.ISI.EDU.
+ 6.0.0.10.IN-ADDR.ARPA. PTR MULTICS.MIT.EDU.
+
+Thus a program which wanted to locate gateways on net 10 would originate
+a query of the form QTYPE=PTR, QCLASS=IN, QNAME=10.IN-ADDR.ARPA. It
+would receive two RRs in response:
+
+ 10.IN-ADDR.ARPA. PTR MILNET-GW.ISI.EDU.
+ 10.IN-ADDR.ARPA. PTR GW.LCS.MIT.EDU.
+
+The program could then originate QTYPE=A, QCLASS=IN queries for MILNET-
+GW.ISI.EDU. and GW.LCS.MIT.EDU. to discover the Internet addresses of
+these gateways.
+
+A resolver which wanted to find the host name corresponding to Internet
+host address 10.0.0.6 would pursue a query of the form QTYPE=PTR,
+QCLASS=IN, QNAME=6.0.0.10.IN-ADDR.ARPA, and would receive:
+
+ 6.0.0.10.IN-ADDR.ARPA. PTR MULTICS.MIT.EDU.
+
+Several cautions apply to the use of these services:
+ - Since the IN-ADDR.ARPA special domain and the normal domain
+ for a particular host or gateway will be in different zones,
+ the possibility exists that that the data may be inconsistent.
+
+ - Gateways will often have two names in separate domains, only
+ one of which can be primary.
+
+ - Systems that use the domain database to initialize their
+ routing tables must start with enough gateway information to
+ guarantee that they can access the appropriate name server.
+
+ - The gateway data only reflects the existence of a gateway in a
+ manner equivalent to the current HOSTS.TXT file. It doesn't
+ replace the dynamic availability information from GGP or EGP.
+
+
+
+Mockapetris [Page 23]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+3.6. Defining new types, classes, and special namespaces
+
+The previously defined types and classes are the ones in use as of the
+date of this memo. New definitions should be expected. This section
+makes some recommendations to designers considering additions to the
+existing facilities. The mailing list NAMEDROPPERS@SRI-NIC.ARPA is the
+forum where general discussion of design issues takes place.
+
+In general, a new type is appropriate when new information is to be
+added to the database about an existing object, or we need new data
+formats for some totally new object. Designers should attempt to define
+types and their RDATA formats that are generally applicable to all
+classes, and which avoid duplication of information. New classes are
+appropriate when the DNS is to be used for a new protocol, etc which
+requires new class-specific data formats, or when a copy of the existing
+name space is desired, but a separate management domain is necessary.
+
+New types and classes need mnemonics for master files; the format of the
+master files requires that the mnemonics for type and class be disjoint.
+
+TYPE and CLASS values must be a proper subset of QTYPEs and QCLASSes
+respectively.
+
+The present system uses multiple RRs to represent multiple values of a
+type rather than storing multiple values in the RDATA section of a
+single RR. This is less efficient for most applications, but does keep
+RRs shorter. The multiple RRs assumption is incorporated in some
+experimental work on dynamic update methods.
+
+The present system attempts to minimize the duplication of data in the
+database in order to insure consistency. Thus, in order to find the
+address of the host for a mail exchange, you map the mail domain name to
+a host name, then the host name to addresses, rather than a direct
+mapping to host address. This approach is preferred because it avoids
+the opportunity for inconsistency.
+
+In defining a new type of data, multiple RR types should not be used to
+create an ordering between entries or express different formats for
+equivalent bindings, instead this information should be carried in the
+body of the RR and a single type used. This policy avoids problems with
+caching multiple types and defining QTYPEs to match multiple types.
+
+For example, the original form of mail exchange binding used two RR
+types one to represent a "closer" exchange (MD) and one to represent a
+"less close" exchange (MF). The difficulty is that the presence of one
+RR type in a cache doesn't convey any information about the other
+because the query which acquired the cached information might have used
+a QTYPE of MF, MD, or MAILA (which matched both). The redesigned
+
+
+
+Mockapetris [Page 24]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+service used a single type (MX) with a "preference" value in the RDATA
+section which can order different RRs. However, if any MX RRs are found
+in the cache, then all should be there.
+
+4. MESSAGES
+
+4.1. Format
+
+All communications inside of the domain protocol are carried in a single
+format called a message. The top level format of message is divided
+into 5 sections (some of which are empty in certain cases) shown below:
+
+ +---------------------+
+ | Header |
+ +---------------------+
+ | Question | the question for the name server
+ +---------------------+
+ | Answer | RRs answering the question
+ +---------------------+
+ | Authority | RRs pointing toward an authority
+ +---------------------+
+ | Additional | RRs holding additional information
+ +---------------------+
+
+The header section is always present. The header includes fields that
+specify which of the remaining sections are present, and also specify
+whether the message is a query or a response, a standard query or some
+other opcode, etc.
+
+The names of the sections after the header are derived from their use in
+standard queries. The question section contains fields that describe a
+question to a name server. These fields are a query type (QTYPE), a
+query class (QCLASS), and a query domain name (QNAME). The last three
+sections have the same format: a possibly empty list of concatenated
+resource records (RRs). The answer section contains RRs that answer the
+question; the authority section contains RRs that point toward an
+authoritative name server; the additional records section contains RRs
+which relate to the query, but are not strictly answers for the
+question.
+
+
+
+
+
+
+
+
+
+
+
+
+Mockapetris [Page 25]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+4.1.1. Header section format
+
+The header contains the following fields:
+
+ 1 1 1 1 1 1
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | ID |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ |QR| Opcode |AA|TC|RD|RA| Z | RCODE |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | QDCOUNT |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | ANCOUNT |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | NSCOUNT |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | ARCOUNT |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+ID A 16 bit identifier assigned by the program that
+ generates any kind of query. This identifier is copied
+ the corresponding reply and can be used by the requester
+ to match up replies to outstanding queries.
+
+QR A one bit field that specifies whether this message is a
+ query (0), or a response (1).
+
+OPCODE A four bit field that specifies kind of query in this
+ message. This value is set by the originator of a query
+ and copied into the response. The values are:
+
+ 0 a standard query (QUERY)
+
+ 1 an inverse query (IQUERY)
+
+ 2 a server status request (STATUS)
+
+ 3-15 reserved for future use
+
+AA Authoritative Answer - this bit is valid in responses,
+ and specifies that the responding name server is an
+ authority for the domain name in question section.
+
+ Note that the contents of the answer section may have
+ multiple owner names because of aliases. The AA bit
+
+
+
+Mockapetris [Page 26]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+ corresponds to the name which matches the query name, or
+ the first owner name in the answer section.
+
+TC TrunCation - specifies that this message was truncated
+ due to length greater than that permitted on the
+ transmission channel.
+
+RD Recursion Desired - this bit may be set in a query and
+ is copied into the response. If RD is set, it directs
+ the name server to pursue the query recursively.
+ Recursive query support is optional.
+
+RA Recursion Available - this be is set or cleared in a
+ response, and denotes whether recursive query support is
+ available in the name server.
+
+Z Reserved for future use. Must be zero in all queries
+ and responses.
+
+RCODE Response code - this 4 bit field is set as part of
+ responses. The values have the following
+ interpretation:
+
+ 0 No error condition
+
+ 1 Format error - The name server was
+ unable to interpret the query.
+
+ 2 Server failure - The name server was
+ unable to process this query due to a
+ problem with the name server.
+
+ 3 Name Error - Meaningful only for
+ responses from an authoritative name
+ server, this code signifies that the
+ domain name referenced in the query does
+ not exist.
+
+ 4 Not Implemented - The name server does
+ not support the requested kind of query.
+
+ 5 Refused - The name server refuses to
+ perform the specified operation for
+ policy reasons. For example, a name
+ server may not wish to provide the
+ information to the particular requester,
+ or a name server may not wish to perform
+ a particular operation (e.g., zone
+
+
+
+Mockapetris [Page 27]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+ transfer) for particular data.
+
+ 6-15 Reserved for future use.
+
+QDCOUNT an unsigned 16 bit integer specifying the number of
+ entries in the question section.
+
+ANCOUNT an unsigned 16 bit integer specifying the number of
+ resource records in the answer section.
+
+NSCOUNT an unsigned 16 bit integer specifying the number of name
+ server resource records in the authority records
+ section.
+
+ARCOUNT an unsigned 16 bit integer specifying the number of
+ resource records in the additional records section.
+
+4.1.2. Question section format
+
+The question section is used to carry the "question" in most queries,
+i.e., the parameters that define what is being asked. The section
+contains QDCOUNT (usually 1) entries, each of the following format:
+
+ 1 1 1 1 1 1
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | |
+ / QNAME /
+ / /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | QTYPE |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | QCLASS |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+QNAME a domain name represented as a sequence of labels, where
+ each label consists of a length octet followed by that
+ number of octets. The domain name terminates with the
+ zero length octet for the null label of the root. Note
+ that this field may be an odd number of octets; no
+ padding is used.
+
+QTYPE a two octet code which specifies the type of the query.
+ The values for this field include all codes valid for a
+ TYPE field, together with some more general codes which
+ can match more than one type of RR.
+
+
+
+Mockapetris [Page 28]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+QCLASS a two octet code that specifies the class of the query.
+ For example, the QCLASS field is IN for the Internet.
+
+4.1.3. Resource record format
+
+The answer, authority, and additional sections all share the same
+format: a variable number of resource records, where the number of
+records is specified in the corresponding count field in the header.
+Each resource record has the following format:
+ 1 1 1 1 1 1
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | |
+ / /
+ / NAME /
+ | |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | TYPE |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | CLASS |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | TTL |
+ | |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | RDLENGTH |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
+ / RDATA /
+ / /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+NAME a domain name to which this resource record pertains.
+
+TYPE two octets containing one of the RR type codes. This
+ field specifies the meaning of the data in the RDATA
+ field.
+
+CLASS two octets which specify the class of the data in the
+ RDATA field.
+
+TTL a 32 bit unsigned integer that specifies the time
+ interval (in seconds) that the resource record may be
+ cached before it should be discarded. Zero values are
+ interpreted to mean that the RR can only be used for the
+ transaction in progress, and should not be cached.
+
+
+
+
+
+Mockapetris [Page 29]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+RDLENGTH an unsigned 16 bit integer that specifies the length in
+ octets of the RDATA field.
+
+RDATA a variable length string of octets that describes the
+ resource. The format of this information varies
+ according to the TYPE and CLASS of the resource record.
+ For example, the if the TYPE is A and the CLASS is IN,
+ the RDATA field is a 4 octet ARPA Internet address.
+
+4.1.4. Message compression
+
+In order to reduce the size of messages, the domain system utilizes a
+compression scheme which eliminates the repetition of domain names in a
+message. In this scheme, an entire domain name or a list of labels at
+the end of a domain name is replaced with a pointer to a prior occurance
+of the same name.
+
+The pointer takes the form of a two octet sequence:
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | 1 1| OFFSET |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+The first two bits are ones. This allows a pointer to be distinguished
+from a label, since the label must begin with two zero bits because
+labels are restricted to 63 octets or less. (The 10 and 01 combinations
+are reserved for future use.) The OFFSET field specifies an offset from
+the start of the message (i.e., the first octet of the ID field in the
+domain header). A zero offset specifies the first byte of the ID field,
+etc.
+
+The compression scheme allows a domain name in a message to be
+represented as either:
+
+ - a sequence of labels ending in a zero octet
+
+ - a pointer
+
+ - a sequence of labels ending with a pointer
+
+Pointers can only be used for occurances of a domain name where the
+format is not class specific. If this were not the case, a name server
+or resolver would be required to know the format of all RRs it handled.
+As yet, there are no such cases, but they may occur in future RDATA
+formats.
+
+If a domain name is contained in a part of the message subject to a
+length field (such as the RDATA section of an RR), and compression is
+
+
+
+Mockapetris [Page 30]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+used, the length of the compressed name is used in the length
+calculation, rather than the length of the expanded name.
+
+Programs are free to avoid using pointers in messages they generate,
+although this will reduce datagram capacity, and may cause truncation.
+However all programs are required to understand arriving messages that
+contain pointers.
+
+For example, a datagram might need to use the domain names F.ISI.ARPA,
+FOO.F.ISI.ARPA, ARPA, and the root. Ignoring the other fields of the
+message, these domain names might be represented as:
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ 20 | 1 | F |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ 22 | 3 | I |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ 24 | S | I |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ 26 | 4 | A |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ 28 | R | P |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ 30 | A | 0 |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ 40 | 3 | F |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ 42 | O | O |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ 44 | 1 1| 20 |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ 64 | 1 1| 26 |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ 92 | 0 | |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+The domain name for F.ISI.ARPA is shown at offset 20. The domain name
+FOO.F.ISI.ARPA is shown at offset 40; this definition uses a pointer to
+concatenate a label for FOO to the previously defined F.ISI.ARPA. The
+domain name ARPA is defined at offset 64 using a pointer to the ARPA
+component of the name F.ISI.ARPA at 20; note that this pointer relies on
+ARPA being the last label in the string at 20. The root domain name is
+
+
+
+Mockapetris [Page 31]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+defined by a single octet of zeros at 92; the root domain name has no
+labels.
+
+4.2. Transport
+
+The DNS assumes that messages will be transmitted as datagrams or in a
+byte stream carried by a virtual circuit. While virtual circuits can be
+used for any DNS activity, datagrams are preferred for queries due to
+their lower overhead and better performance. Zone refresh activities
+must use virtual circuits because of the need for reliable transfer.
+
+The Internet supports name server access using TCP [RFC-793] on server
+port 53 (decimal) as well as datagram access using UDP [RFC-768] on UDP
+port 53 (decimal).
+
+4.2.1. UDP usage
+
+Messages sent using UDP user server port 53 (decimal).
+
+Messages carried by UDP are restricted to 512 bytes (not counting the IP
+or UDP headers). Longer messages are truncated and the TC bit is set in
+the header.
+
+UDP is not acceptable for zone transfers, but is the recommended method
+for standard queries in the Internet. Queries sent using UDP may be
+lost, and hence a retransmission strategy is required. Queries or their
+responses may be reordered by the network, or by processing in name
+servers, so resolvers should not depend on them being returned in order.
+
+The optimal UDP retransmission policy will vary with performance of the
+Internet and the needs of the client, but the following are recommended:
+
+ - The client should try other servers and server addresses
+ before repeating a query to a specific address of a server.
+
+ - The retransmission interval should be based on prior
+ statistics if possible. Too aggressive retransmission can
+ easily slow responses for the community at large. Depending
+ on how well connected the client is to its expected servers,
+ the minimum retransmission interval should be 2-5 seconds.
+
+More suggestions on server selection and retransmission policy can be
+found in the resolver section of this memo.
+
+4.2.2. TCP usage
+
+Messages sent over TCP connections use server port 53 (decimal). The
+message is prefixed with a two byte length field which gives the message
+
+
+
+Mockapetris [Page 32]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+length, excluding the two byte length field. This length field allows
+the low-level processing to assemble a complete message before beginning
+to parse it.
+
+Several connection management policies are recommended:
+
+ - The server should not block other activities waiting for TCP
+ data.
+
+ - The server should support multiple connections.
+
+ - The server should assume that the client will initiate
+ connection closing, and should delay closing its end of the
+ connection until all outstanding client requests have been
+ satisfied.
+
+ - If the server needs to close a dormant connection to reclaim
+ resources, it should wait until the connection has been idle
+ for a period on the order of two minutes. In particular, the
+ server should allow the SOA and AXFR request sequence (which
+ begins a refresh operation) to be made on a single connection.
+ Since the server would be unable to answer queries anyway, a
+ unilateral close or reset may be used instead of a graceful
+ close.
+
+5. MASTER FILES
+
+Master files are text files that contain RRs in text form. Since the
+contents of a zone can be expressed in the form of a list of RRs a
+master file is most often used to define a zone, though it can be used
+to list a cache's contents. Hence, this section first discusses the
+format of RRs in a master file, and then the special considerations when
+a master file is used to create a zone in some name server.
+
+5.1. Format
+
+The format of these files is a sequence of entries. Entries are
+predominantly line-oriented, though parentheses can be used to continue
+a list of items across a line boundary, and text literals can contain
+CRLF within the text. Any combination of tabs and spaces act as a
+delimiter between the separate items that make up an entry. The end of
+any line in the master file can end with a comment. The comment starts
+with a ";" (semicolon).
+
+The following entries are defined:
+
+ <blank>[<comment>]
+
+
+
+
+Mockapetris [Page 33]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+ $ORIGIN <domain-name> [<comment>]
+
+ $INCLUDE <file-name> [<domain-name>] [<comment>]
+
+ <domain-name><rr> [<comment>]
+
+ <blank><rr> [<comment>]
+
+Blank lines, with or without comments, are allowed anywhere in the file.
+
+Two control entries are defined: $ORIGIN and $INCLUDE. $ORIGIN is
+followed by a domain name, and resets the current origin for relative
+domain names to the stated name. $INCLUDE inserts the named file into
+the current file, and may optionally specify a domain name that sets the
+relative domain name origin for the included file. $INCLUDE may also
+have a comment. Note that a $INCLUDE entry never changes the relative
+origin of the parent file, regardless of changes to the relative origin
+made within the included file.
+
+The last two forms represent RRs. If an entry for an RR begins with a
+blank, then the RR is assumed to be owned by the last stated owner. If
+an RR entry begins with a <domain-name>, then the owner name is reset.
+
+<rr> contents take one of the following forms:
+
+ [<TTL>] [<class>] <type> <RDATA>
+
+ [<class>] [<TTL>] <type> <RDATA>
+
+The RR begins with optional TTL and class fields, followed by a type and
+RDATA field appropriate to the type and class. Class and type use the
+standard mnemonics, TTL is a decimal integer. Omitted class and TTL
+values are default to the last explicitly stated values. Since type and
+class mnemonics are disjoint, the parse is unique. (Note that this
+order is different from the order used in examples and the order used in
+the actual RRs; the given order allows easier parsing and defaulting.)
+
+<domain-name>s make up a large share of the data in the master file.
+The labels in the domain name are expressed as character strings and
+separated by dots. Quoting conventions allow arbitrary characters to be
+stored in domain names. Domain names that end in a dot are called
+absolute, and are taken as complete. Domain names which do not end in a
+dot are called relative; the actual domain name is the concatenation of
+the relative part with an origin specified in a $ORIGIN, $INCLUDE, or as
+an argument to the master file loading routine. A relative name is an
+error when no origin is available.
+
+
+
+
+
+Mockapetris [Page 34]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+<character-string> is expressed in one or two ways: as a contiguous set
+of characters without interior spaces, or as a string beginning with a "
+and ending with a ". Inside a " delimited string any character can
+occur, except for a " itself, which must be quoted using \ (back slash).
+
+Because these files are text files several special encodings are
+necessary to allow arbitrary data to be loaded. In particular:
+
+ of the root.
+
+@ A free standing @ is used to denote the current origin.
+
+\X where X is any character other than a digit (0-9), is
+ used to quote that character so that its special meaning
+ does not apply. For example, "\." can be used to place
+ a dot character in a label.
+
+\DDD where each D is a digit is the octet corresponding to
+ the decimal number described by DDD. The resulting
+ octet is assumed to be text and is not checked for
+ special meaning.
+
+( ) Parentheses are used to group data that crosses a line
+ boundary. In effect, line terminations are not
+ recognized within parentheses.
+
+; Semicolon is used to start a comment; the remainder of
+ the line is ignored.
+
+5.2. Use of master files to define zones
+
+When a master file is used to load a zone, the operation should be
+suppressed if any errors are encountered in the master file. The
+rationale for this is that a single error can have widespread
+consequences. For example, suppose that the RRs defining a delegation
+have syntax errors; then the server will return authoritative name
+errors for all names in the subzone (except in the case where the
+subzone is also present on the server).
+
+Several other validity checks that should be performed in addition to
+insuring that the file is syntactically correct:
+
+ 1. All RRs in the file should have the same class.
+
+ 2. Exactly one SOA RR should be present at the top of the zone.
+
+ 3. If delegations are present and glue information is required,
+ it should be present.
+
+
+
+Mockapetris [Page 35]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+ 4. Information present outside of the authoritative nodes in the
+ zone should be glue information, rather than the result of an
+ origin or similar error.
+
+5.3. Master file example
+
+The following is an example file which might be used to define the
+ISI.EDU zone.and is loaded with an origin of ISI.EDU:
+
+@ IN SOA VENERA Action\.domains (
+ 20 ; SERIAL
+ 7200 ; REFRESH
+ 600 ; RETRY
+ 3600000; EXPIRE
+ 60) ; MINIMUM
+
+ NS A.ISI.EDU.
+ NS VENERA
+ NS VAXA
+ MX 10 VENERA
+ MX 20 VAXA
+
+A A 26.3.0.103
+
+VENERA A 10.1.0.52
+ A 128.9.0.32
+
+VAXA A 10.2.0.27
+ A 128.9.0.33
+
+
+$INCLUDE <SUBSYS>ISI-MAILBOXES.TXT
+
+Where the file <SUBSYS>ISI-MAILBOXES.TXT is:
+
+ MOE MB A.ISI.EDU.
+ LARRY MB A.ISI.EDU.
+ CURLEY MB A.ISI.EDU.
+ STOOGES MG MOE
+ MG LARRY
+ MG CURLEY
+
+Note the use of the \ character in the SOA RR to specify the responsible
+person mailbox "Action.domains@E.ISI.EDU".
+
+
+
+
+
+
+
+Mockapetris [Page 36]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+6. NAME SERVER IMPLEMENTATION
+
+6.1. Architecture
+
+The optimal structure for the name server will depend on the host
+operating system and whether the name server is integrated with resolver
+operations, either by supporting recursive service, or by sharing its
+database with a resolver. This section discusses implementation
+considerations for a name server which shares a database with a
+resolver, but most of these concerns are present in any name server.
+
+6.1.1. Control
+
+A name server must employ multiple concurrent activities, whether they
+are implemented as separate tasks in the host's OS or multiplexing
+inside a single name server program. It is simply not acceptable for a
+name server to block the service of UDP requests while it waits for TCP
+data for refreshing or query activities. Similarly, a name server
+should not attempt to provide recursive service without processing such
+requests in parallel, though it may choose to serialize requests from a
+single client, or to regard identical requests from the same client as
+duplicates. A name server should not substantially delay requests while
+it reloads a zone from master files or while it incorporates a newly
+refreshed zone into its database.
+
+6.1.2. Database
+
+While name server implementations are free to use any internal data
+structures they choose, the suggested structure consists of three major
+parts:
+
+ - A "catalog" data structure which lists the zones available to
+ this server, and a "pointer" to the zone data structure. The
+ main purpose of this structure is to find the nearest ancestor
+ zone, if any, for arriving standard queries.
+
+ - Separate data structures for each of the zones held by the
+ name server.
+
+ - A data structure for cached data. (or perhaps separate caches
+ for different classes)
+
+All of these data structures can be implemented an identical tree
+structure format, with different data chained off the nodes in different
+parts: in the catalog the data is pointers to zones, while in the zone
+and cache data structures, the data will be RRs. In designing the tree
+framework the designer should recognize that query processing will need
+to traverse the tree using case-insensitive label comparisons; and that
+
+
+
+Mockapetris [Page 37]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+in real data, a few nodes have a very high branching factor (100-1000 or
+more), but the vast majority have a very low branching factor (0-1).
+
+One way to solve the case problem is to store the labels for each node
+in two pieces: a standardized-case representation of the label where all
+ASCII characters are in a single case, together with a bit mask that
+denotes which characters are actually of a different case. The
+branching factor diversity can be handled using a simple linked list for
+a node until the branching factor exceeds some threshold, and
+transitioning to a hash structure after the threshold is exceeded. In
+any case, hash structures used to store tree sections must insure that
+hash functions and procedures preserve the casing conventions of the
+DNS.
+
+The use of separate structures for the different parts of the database
+is motivated by several factors:
+
+ - The catalog structure can be an almost static structure that
+ need change only when the system administrator changes the
+ zones supported by the server. This structure can also be
+ used to store parameters used to control refreshing
+ activities.
+
+ - The individual data structures for zones allow a zone to be
+ replaced simply by changing a pointer in the catalog. Zone
+ refresh operations can build a new structure and, when
+ complete, splice it into the database via a simple pointer
+ replacement. It is very important that when a zone is
+ refreshed, queries should not use old and new data
+ simultaneously.
+
+ - With the proper search procedures, authoritative data in zones
+ will always "hide", and hence take precedence over, cached
+ data.
+
+ - Errors in zone definitions that cause overlapping zones, etc.,
+ may cause erroneous responses to queries, but problem
+ determination is simplified, and the contents of one "bad"
+ zone can't corrupt another.
+
+ - Since the cache is most frequently updated, it is most
+ vulnerable to corruption during system restarts. It can also
+ become full of expired RR data. In either case, it can easily
+ be discarded without disturbing zone data.
+
+A major aspect of database design is selecting a structure which allows
+the name server to deal with crashes of the name server's host. State
+information which a name server should save across system crashes
+
+
+
+Mockapetris [Page 38]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+includes the catalog structure (including the state of refreshing for
+each zone) and the zone data itself.
+
+6.1.3. Time
+
+Both the TTL data for RRs and the timing data for refreshing activities
+depends on 32 bit timers in units of seconds. Inside the database,
+refresh timers and TTLs for cached data conceptually "count down", while
+data in the zone stays with constant TTLs.
+
+A recommended implementation strategy is to store time in two ways: as
+a relative increment and as an absolute time. One way to do this is to
+use positive 32 bit numbers for one type and negative numbers for the
+other. The RRs in zones use relative times; the refresh timers and
+cache data use absolute times. Absolute numbers are taken with respect
+to some known origin and converted to relative values when placed in the
+response to a query. When an absolute TTL is negative after conversion
+to relative, then the data is expired and should be ignored.
+
+6.2. Standard query processing
+
+The major algorithm for standard query processing is presented in
+[RFC-1034].
+
+When processing queries with QCLASS=*, or some other QCLASS which
+matches multiple classes, the response should never be authoritative
+unless the server can guarantee that the response covers all classes.
+
+When composing a response, RRs which are to be inserted in the
+additional section, but duplicate RRs in the answer or authority
+sections, may be omitted from the additional section.
+
+When a response is so long that truncation is required, the truncation
+should start at the end of the response and work forward in the
+datagram. Thus if there is any data for the authority section, the
+answer section is guaranteed to be unique.
+
+The MINIMUM value in the SOA should be used to set a floor on the TTL of
+data distributed from a zone. This floor function should be done when
+the data is copied into a response. This will allow future dynamic
+update protocols to change the SOA MINIMUM field without ambiguous
+semantics.
+
+6.3. Zone refresh and reload processing
+
+In spite of a server's best efforts, it may be unable to load zone data
+from a master file due to syntax errors, etc., or be unable to refresh a
+zone within the its expiration parameter. In this case, the name server
+
+
+
+Mockapetris [Page 39]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+should answer queries as if it were not supposed to possess the zone.
+
+If a master is sending a zone out via AXFR, and a new version is created
+during the transfer, the master should continue to send the old version
+if possible. In any case, it should never send part of one version and
+part of another. If completion is not possible, the master should reset
+the connection on which the zone transfer is taking place.
+
+6.4. Inverse queries (Optional)
+
+Inverse queries are an optional part of the DNS. Name servers are not
+required to support any form of inverse queries. If a name server
+receives an inverse query that it does not support, it returns an error
+response with the "Not Implemented" error set in the header. While
+inverse query support is optional, all name servers must be at least
+able to return the error response.
+
+6.4.1. The contents of inverse queries and responses Inverse
+queries reverse the mappings performed by standard query operations;
+while a standard query maps a domain name to a resource, an inverse
+query maps a resource to a domain name. For example, a standard query
+might bind a domain name to a host address; the corresponding inverse
+query binds the host address to a domain name.
+
+Inverse queries take the form of a single RR in the answer section of
+the message, with an empty question section. The owner name of the
+query RR and its TTL are not significant. The response carries
+questions in the question section which identify all names possessing
+the query RR WHICH THE NAME SERVER KNOWS. Since no name server knows
+about all of the domain name space, the response can never be assumed to
+be complete. Thus inverse queries are primarily useful for database
+management and debugging activities. Inverse queries are NOT an
+acceptable method of mapping host addresses to host names; use the IN-
+ADDR.ARPA domain instead.
+
+Where possible, name servers should provide case-insensitive comparisons
+for inverse queries. Thus an inverse query asking for an MX RR of
+"Venera.isi.edu" should get the same response as a query for
+"VENERA.ISI.EDU"; an inverse query for HINFO RR "IBM-PC UNIX" should
+produce the same result as an inverse query for "IBM-pc unix". However,
+this cannot be guaranteed because name servers may possess RRs that
+contain character strings but the name server does not know that the
+data is character.
+
+When a name server processes an inverse query, it either returns:
+
+ 1. zero, one, or multiple domain names for the specified
+ resource as QNAMEs in the question section
+
+
+
+Mockapetris [Page 40]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+ 2. an error code indicating that the name server doesn't support
+ inverse mapping of the specified resource type.
+
+When the response to an inverse query contains one or more QNAMEs, the
+owner name and TTL of the RR in the answer section which defines the
+inverse query is modified to exactly match an RR found at the first
+QNAME.
+
+RRs returned in the inverse queries cannot be cached using the same
+mechanism as is used for the replies to standard queries. One reason
+for this is that a name might have multiple RRs of the same type, and
+only one would appear. For example, an inverse query for a single
+address of a multiply homed host might create the impression that only
+one address existed.
+
+6.4.2. Inverse query and response example The overall structure
+of an inverse query for retrieving the domain name that corresponds to
+Internet address 10.1.0.52 is shown below:
+
+ +-----------------------------------------+
+ Header | OPCODE=IQUERY, ID=997 |
+ +-----------------------------------------+
+ Question | <empty> |
+ +-----------------------------------------+
+ Answer | <anyname> A IN 10.1.0.52 |
+ +-----------------------------------------+
+ Authority | <empty> |
+ +-----------------------------------------+
+ Additional | <empty> |
+ +-----------------------------------------+
+
+This query asks for a question whose answer is the Internet style
+address 10.1.0.52. Since the owner name is not known, any domain name
+can be used as a placeholder (and is ignored). A single octet of zero,
+signifying the root, is usually used because it minimizes the length of
+the message. The TTL of the RR is not significant. The response to
+this query might be:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Mockapetris [Page 41]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+ +-----------------------------------------+
+ Header | OPCODE=RESPONSE, ID=997 |
+ +-----------------------------------------+
+ Question |QTYPE=A, QCLASS=IN, QNAME=VENERA.ISI.EDU |
+ +-----------------------------------------+
+ Answer | VENERA.ISI.EDU A IN 10.1.0.52 |
+ +-----------------------------------------+
+ Authority | <empty> |
+ +-----------------------------------------+
+ Additional | <empty> |
+ +-----------------------------------------+
+
+Note that the QTYPE in a response to an inverse query is the same as the
+TYPE field in the answer section of the inverse query. Responses to
+inverse queries may contain multiple questions when the inverse is not
+unique. If the question section in the response is not empty, then the
+RR in the answer section is modified to correspond to be an exact copy
+of an RR at the first QNAME.
+
+6.4.3. Inverse query processing
+
+Name servers that support inverse queries can support these operations
+through exhaustive searches of their databases, but this becomes
+impractical as the size of the database increases. An alternative
+approach is to invert the database according to the search key.
+
+For name servers that support multiple zones and a large amount of data,
+the recommended approach is separate inversions for each zone. When a
+particular zone is changed during a refresh, only its inversions need to
+be redone.
+
+Support for transfer of this type of inversion may be included in future
+versions of the domain system, but is not supported in this version.
+
+6.5. Completion queries and responses
+
+The optional completion services described in RFC-882 and RFC-883 have
+been deleted. Redesigned services may become available in the future.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Mockapetris [Page 42]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+7. RESOLVER IMPLEMENTATION
+
+The top levels of the recommended resolver algorithm are discussed in
+[RFC-1034]. This section discusses implementation details assuming the
+database structure suggested in the name server implementation section
+of this memo.
+
+7.1. Transforming a user request into a query
+
+The first step a resolver takes is to transform the client's request,
+stated in a format suitable to the local OS, into a search specification
+for RRs at a specific name which match a specific QTYPE and QCLASS.
+Where possible, the QTYPE and QCLASS should correspond to a single type
+and a single class, because this makes the use of cached data much
+simpler. The reason for this is that the presence of data of one type
+in a cache doesn't confirm the existence or non-existence of data of
+other types, hence the only way to be sure is to consult an
+authoritative source. If QCLASS=* is used, then authoritative answers
+won't be available.
+
+Since a resolver must be able to multiplex multiple requests if it is to
+perform its function efficiently, each pending request is usually
+represented in some block of state information. This state block will
+typically contain:
+
+ - A timestamp indicating the time the request began.
+ The timestamp is used to decide whether RRs in the database
+ can be used or are out of date. This timestamp uses the
+ absolute time format previously discussed for RR storage in
+ zones and caches. Note that when an RRs TTL indicates a
+ relative time, the RR must be timely, since it is part of a
+ zone. When the RR has an absolute time, it is part of a
+ cache, and the TTL of the RR is compared against the timestamp
+ for the start of the request.
+
+ Note that using the timestamp is superior to using a current
+ time, since it allows RRs with TTLs of zero to be entered in
+ the cache in the usual manner, but still used by the current
+ request, even after intervals of many seconds due to system
+ load, query retransmission timeouts, etc.
+
+ - Some sort of parameters to limit the amount of work which will
+ be performed for this request.
+
+ The amount of work which a resolver will do in response to a
+ client request must be limited to guard against errors in the
+ database, such as circular CNAME references, and operational
+ problems, such as network partition which prevents the
+
+
+
+Mockapetris [Page 43]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+ resolver from accessing the name servers it needs. While
+ local limits on the number of times a resolver will retransmit
+ a particular query to a particular name server address are
+ essential, the resolver should have a global per-request
+ counter to limit work on a single request. The counter should
+ be set to some initial value and decremented whenever the
+ resolver performs any action (retransmission timeout,
+ retransmission, etc.) If the counter passes zero, the request
+ is terminated with a temporary error.
+
+ Note that if the resolver structure allows one request to
+ start others in parallel, such as when the need to access a
+ name server for one request causes a parallel resolve for the
+ name server's addresses, the spawned request should be started
+ with a lower counter. This prevents circular references in
+ the database from starting a chain reaction of resolver
+ activity.
+
+ - The SLIST data structure discussed in [RFC-1034].
+
+ This structure keeps track of the state of a request if it
+ must wait for answers from foreign name servers.
+
+7.2. Sending the queries
+
+As described in [RFC-1034], the basic task of the resolver is to
+formulate a query which will answer the client's request and direct that
+query to name servers which can provide the information. The resolver
+will usually only have very strong hints about which servers to ask, in
+the form of NS RRs, and may have to revise the query, in response to
+CNAMEs, or revise the set of name servers the resolver is asking, in
+response to delegation responses which point the resolver to name
+servers closer to the desired information. In addition to the
+information requested by the client, the resolver may have to call upon
+its own services to determine the address of name servers it wishes to
+contact.
+
+In any case, the model used in this memo assumes that the resolver is
+multiplexing attention between multiple requests, some from the client,
+and some internally generated. Each request is represented by some
+state information, and the desired behavior is that the resolver
+transmit queries to name servers in a way that maximizes the probability
+that the request is answered, minimizes the time that the request takes,
+and avoids excessive transmissions. The key algorithm uses the state
+information of the request to select the next name server address to
+query, and also computes a timeout which will cause the next action
+should a response not arrive. The next action will usually be a
+transmission to some other server, but may be a temporary error to the
+
+
+
+Mockapetris [Page 44]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+client.
+
+The resolver always starts with a list of server names to query (SLIST).
+This list will be all NS RRs which correspond to the nearest ancestor
+zone that the resolver knows about. To avoid startup problems, the
+resolver should have a set of default servers which it will ask should
+it have no current NS RRs which are appropriate. The resolver then adds
+to SLIST all of the known addresses for the name servers, and may start
+parallel requests to acquire the addresses of the servers when the
+resolver has the name, but no addresses, for the name servers.
+
+To complete initialization of SLIST, the resolver attaches whatever
+history information it has to the each address in SLIST. This will
+usually consist of some sort of weighted averages for the response time
+of the address, and the batting average of the address (i.e., how often
+the address responded at all to the request). Note that this
+information should be kept on a per address basis, rather than on a per
+name server basis, because the response time and batting average of a
+particular server may vary considerably from address to address. Note
+also that this information is actually specific to a resolver address /
+server address pair, so a resolver with multiple addresses may wish to
+keep separate histories for each of its addresses. Part of this step
+must deal with addresses which have no such history; in this case an
+expected round trip time of 5-10 seconds should be the worst case, with
+lower estimates for the same local network, etc.
+
+Note that whenever a delegation is followed, the resolver algorithm
+reinitializes SLIST.
+
+The information establishes a partial ranking of the available name
+server addresses. Each time an address is chosen and the state should
+be altered to prevent its selection again until all other addresses have
+been tried. The timeout for each transmission should be 50-100% greater
+than the average predicted value to allow for variance in response.
+
+Some fine points:
+
+ - The resolver may encounter a situation where no addresses are
+ available for any of the name servers named in SLIST, and
+ where the servers in the list are precisely those which would
+ normally be used to look up their own addresses. This
+ situation typically occurs when the glue address RRs have a
+ smaller TTL than the NS RRs marking delegation, or when the
+ resolver caches the result of a NS search. The resolver
+ should detect this condition and restart the search at the
+ next ancestor zone, or alternatively at the root.
+
+
+
+
+
+Mockapetris [Page 45]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+ - If a resolver gets a server error or other bizarre response
+ from a name server, it should remove it from SLIST, and may
+ wish to schedule an immediate transmission to the next
+ candidate server address.
+
+7.3. Processing responses
+
+The first step in processing arriving response datagrams is to parse the
+response. This procedure should include:
+
+ - Check the header for reasonableness. Discard datagrams which
+ are queries when responses are expected.
+
+ - Parse the sections of the message, and insure that all RRs are
+ correctly formatted.
+
+ - As an optional step, check the TTLs of arriving data looking
+ for RRs with excessively long TTLs. If a RR has an
+ excessively long TTL, say greater than 1 week, either discard
+ the whole response, or limit all TTLs in the response to 1
+ week.
+
+The next step is to match the response to a current resolver request.
+The recommended strategy is to do a preliminary matching using the ID
+field in the domain header, and then to verify that the question section
+corresponds to the information currently desired. This requires that
+the transmission algorithm devote several bits of the domain ID field to
+a request identifier of some sort. This step has several fine points:
+
+ - Some name servers send their responses from different
+ addresses than the one used to receive the query. That is, a
+ resolver cannot rely that a response will come from the same
+ address which it sent the corresponding query to. This name
+ server bug is typically encountered in UNIX systems.
+
+ - If the resolver retransmits a particular request to a name
+ server it should be able to use a response from any of the
+ transmissions. However, if it is using the response to sample
+ the round trip time to access the name server, it must be able
+ to determine which transmission matches the response (and keep
+ transmission times for each outgoing message), or only
+ calculate round trip times based on initial transmissions.
+
+ - A name server will occasionally not have a current copy of a
+ zone which it should have according to some NS RRs. The
+ resolver should simply remove the name server from the current
+ SLIST, and continue.
+
+
+
+
+Mockapetris [Page 46]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+7.4. Using the cache
+
+In general, we expect a resolver to cache all data which it receives in
+responses since it may be useful in answering future client requests.
+However, there are several types of data which should not be cached:
+
+ - When several RRs of the same type are available for a
+ particular owner name, the resolver should either cache them
+ all or none at all. When a response is truncated, and a
+ resolver doesn't know whether it has a complete set, it should
+ not cache a possibly partial set of RRs.
+
+ - Cached data should never be used in preference to
+ authoritative data, so if caching would cause this to happen
+ the data should not be cached.
+
+ - The results of an inverse query should not be cached.
+
+ - The results of standard queries where the QNAME contains "*"
+ labels if the data might be used to construct wildcards. The
+ reason is that the cache does not necessarily contain existing
+ RRs or zone boundary information which is necessary to
+ restrict the application of the wildcard RRs.
+
+ - RR data in responses of dubious reliability. When a resolver
+ receives unsolicited responses or RR data other than that
+ requested, it should discard it without caching it. The basic
+ implication is that all sanity checks on a packet should be
+ performed before any of it is cached.
+
+In a similar vein, when a resolver has a set of RRs for some name in a
+response, and wants to cache the RRs, it should check its cache for
+already existing RRs. Depending on the circumstances, either the data
+in the response or the cache is preferred, but the two should never be
+combined. If the data in the response is from authoritative data in the
+answer section, it is always preferred.
+
+8. MAIL SUPPORT
+
+The domain system defines a standard for mapping mailboxes into domain
+names, and two methods for using the mailbox information to derive mail
+routing information. The first method is called mail exchange binding
+and the other method is mailbox binding. The mailbox encoding standard
+and mail exchange binding are part of the DNS official protocol, and are
+the recommended method for mail routing in the Internet. Mailbox
+binding is an experimental feature which is still under development and
+subject to change.
+
+
+
+
+Mockapetris [Page 47]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+The mailbox encoding standard assumes a mailbox name of the form
+"<local-part>@<mail-domain>". While the syntax allowed in each of these
+sections varies substantially between the various mail internets, the
+preferred syntax for the ARPA Internet is given in [RFC-822].
+
+The DNS encodes the <local-part> as a single label, and encodes the
+<mail-domain> as a domain name. The single label from the <local-part>
+is prefaced to the domain name from <mail-domain> to form the domain
+name corresponding to the mailbox. Thus the mailbox HOSTMASTER@SRI-
+NIC.ARPA is mapped into the domain name HOSTMASTER.SRI-NIC.ARPA. If the
+<local-part> contains dots or other special characters, its
+representation in a master file will require the use of backslash
+quoting to ensure that the domain name is properly encoded. For
+example, the mailbox Action.domains@ISI.EDU would be represented as
+Action\.domains.ISI.EDU.
+
+8.1. Mail exchange binding
+
+Mail exchange binding uses the <mail-domain> part of a mailbox
+specification to determine where mail should be sent. The <local-part>
+is not even consulted. [RFC-974] specifies this method in detail, and
+should be consulted before attempting to use mail exchange support.
+
+One of the advantages of this method is that it decouples mail
+destination naming from the hosts used to support mail service, at the
+cost of another layer of indirection in the lookup function. However,
+the addition layer should eliminate the need for complicated "%", "!",
+etc encodings in <local-part>.
+
+The essence of the method is that the <mail-domain> is used as a domain
+name to locate type MX RRs which list hosts willing to accept mail for
+<mail-domain>, together with preference values which rank the hosts
+according to an order specified by the administrators for <mail-domain>.
+
+In this memo, the <mail-domain> ISI.EDU is used in examples, together
+with the hosts VENERA.ISI.EDU and VAXA.ISI.EDU as mail exchanges for
+ISI.EDU. If a mailer had a message for Mockapetris@ISI.EDU, it would
+route it by looking up MX RRs for ISI.EDU. The MX RRs at ISI.EDU name
+VENERA.ISI.EDU and VAXA.ISI.EDU, and type A queries can find the host
+addresses.
+
+8.2. Mailbox binding (Experimental)
+
+In mailbox binding, the mailer uses the entire mail destination
+specification to construct a domain name. The encoded domain name for
+the mailbox is used as the QNAME field in a QTYPE=MAILB query.
+
+Several outcomes are possible for this query:
+
+
+
+Mockapetris [Page 48]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+ 1. The query can return a name error indicating that the mailbox
+ does not exist as a domain name.
+
+ In the long term, this would indicate that the specified
+ mailbox doesn't exist. However, until the use of mailbox
+ binding is universal, this error condition should be
+ interpreted to mean that the organization identified by the
+ global part does not support mailbox binding. The
+ appropriate procedure is to revert to exchange binding at
+ this point.
+
+ 2. The query can return a Mail Rename (MR) RR.
+
+ The MR RR carries new mailbox specification in its RDATA
+ field. The mailer should replace the old mailbox with the
+ new one and retry the operation.
+
+ 3. The query can return a MB RR.
+
+ The MB RR carries a domain name for a host in its RDATA
+ field. The mailer should deliver the message to that host
+ via whatever protocol is applicable, e.g., b,SMTP.
+
+ 4. The query can return one or more Mail Group (MG) RRs.
+
+ This condition means that the mailbox was actually a mailing
+ list or mail group, rather than a single mailbox. Each MG RR
+ has a RDATA field that identifies a mailbox that is a member
+ of the group. The mailer should deliver a copy of the
+ message to each member.
+
+ 5. The query can return a MB RR as well as one or more MG RRs.
+
+ This condition means the the mailbox was actually a mailing
+ list. The mailer can either deliver the message to the host
+ specified by the MB RR, which will in turn do the delivery to
+ all members, or the mailer can use the MG RRs to do the
+ expansion itself.
+
+In any of these cases, the response may include a Mail Information
+(MINFO) RR. This RR is usually associated with a mail group, but is
+legal with a MB. The MINFO RR identifies two mailboxes. One of these
+identifies a responsible person for the original mailbox name. This
+mailbox should be used for requests to be added to a mail group, etc.
+The second mailbox name in the MINFO RR identifies a mailbox that should
+receive error messages for mail failures. This is particularly
+appropriate for mailing lists when errors in member names should be
+reported to a person other than the one who sends a message to the list.
+
+
+
+Mockapetris [Page 49]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+New fields may be added to this RR in the future.
+
+
+9. REFERENCES and BIBLIOGRAPHY
+
+[Dyer 87] S. Dyer, F. Hsu, "Hesiod", Project Athena
+ Technical Plan - Name Service, April 1987, version 1.9.
+
+ Describes the fundamentals of the Hesiod name service.
+
+[IEN-116] J. Postel, "Internet Name Server", IEN-116,
+ USC/Information Sciences Institute, August 1979.
+
+ A name service obsoleted by the Domain Name System, but
+ still in use.
+
+[Quarterman 86] J. Quarterman, and J. Hoskins, "Notable Computer Networks",
+ Communications of the ACM, October 1986, volume 29, number
+ 10.
+
+[RFC-742] K. Harrenstien, "NAME/FINGER", RFC-742, Network
+ Information Center, SRI International, December 1977.
+
+[RFC-768] J. Postel, "User Datagram Protocol", RFC-768,
+ USC/Information Sciences Institute, August 1980.
+
+[RFC-793] J. Postel, "Transmission Control Protocol", RFC-793,
+ USC/Information Sciences Institute, September 1981.
+
+[RFC-799] D. Mills, "Internet Name Domains", RFC-799, COMSAT,
+ September 1981.
+
+ Suggests introduction of a hierarchy in place of a flat
+ name space for the Internet.
+
+[RFC-805] J. Postel, "Computer Mail Meeting Notes", RFC-805,
+ USC/Information Sciences Institute, February 1982.
+
+[RFC-810] E. Feinler, K. Harrenstien, Z. Su, and V. White, "DOD
+ Internet Host Table Specification", RFC-810, Network
+ Information Center, SRI International, March 1982.
+
+ Obsolete. See RFC-952.
+
+[RFC-811] K. Harrenstien, V. White, and E. Feinler, "Hostnames
+ Server", RFC-811, Network Information Center, SRI
+ International, March 1982.
+
+
+
+
+Mockapetris [Page 50]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+ Obsolete. See RFC-953.
+
+[RFC-812] K. Harrenstien, and V. White, "NICNAME/WHOIS", RFC-812,
+ Network Information Center, SRI International, March
+ 1982.
+
+[RFC-819] Z. Su, and J. Postel, "The Domain Naming Convention for
+ Internet User Applications", RFC-819, Network
+ Information Center, SRI International, August 1982.
+
+ Early thoughts on the design of the domain system.
+ Current implementation is completely different.
+
+[RFC-821] J. Postel, "Simple Mail Transfer Protocol", RFC-821,
+ USC/Information Sciences Institute, August 1980.
+
+[RFC-830] Z. Su, "A Distributed System for Internet Name Service",
+ RFC-830, Network Information Center, SRI International,
+ October 1982.
+
+ Early thoughts on the design of the domain system.
+ Current implementation is completely different.
+
+[RFC-882] P. Mockapetris, "Domain names - Concepts and
+ Facilities," RFC-882, USC/Information Sciences
+ Institute, November 1983.
+
+ Superceeded by this memo.
+
+[RFC-883] P. Mockapetris, "Domain names - Implementation and
+ Specification," RFC-883, USC/Information Sciences
+ Institute, November 1983.
+
+ Superceeded by this memo.
+
+[RFC-920] J. Postel and J. Reynolds, "Domain Requirements",
+ RFC-920, USC/Information Sciences Institute,
+ October 1984.
+
+ Explains the naming scheme for top level domains.
+
+[RFC-952] K. Harrenstien, M. Stahl, E. Feinler, "DoD Internet Host
+ Table Specification", RFC-952, SRI, October 1985.
+
+ Specifies the format of HOSTS.TXT, the host/address
+ table replaced by the DNS.
+
+
+
+
+
+Mockapetris [Page 51]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+[RFC-953] K. Harrenstien, M. Stahl, E. Feinler, "HOSTNAME Server",
+ RFC-953, SRI, October 1985.
+
+ This RFC contains the official specification of the
+ hostname server protocol, which is obsoleted by the DNS.
+ This TCP based protocol accesses information stored in
+ the RFC-952 format, and is used to obtain copies of the
+ host table.
+
+[RFC-973] P. Mockapetris, "Domain System Changes and
+ Observations", RFC-973, USC/Information Sciences
+ Institute, January 1986.
+
+ Describes changes to RFC-882 and RFC-883 and reasons for
+ them.
+
+[RFC-974] C. Partridge, "Mail routing and the domain system",
+ RFC-974, CSNET CIC BBN Labs, January 1986.
+
+ Describes the transition from HOSTS.TXT based mail
+ addressing to the more powerful MX system used with the
+ domain system.
+
+[RFC-1001] NetBIOS Working Group, "Protocol standard for a NetBIOS
+ service on a TCP/UDP transport: Concepts and Methods",
+ RFC-1001, March 1987.
+
+ This RFC and RFC-1002 are a preliminary design for
+ NETBIOS on top of TCP/IP which proposes to base NetBIOS
+ name service on top of the DNS.
+
+[RFC-1002] NetBIOS Working Group, "Protocol standard for a NetBIOS
+ service on a TCP/UDP transport: Detailed
+ Specifications", RFC-1002, March 1987.
+
+[RFC-1010] J. Reynolds, and J. Postel, "Assigned Numbers", RFC-1010,
+ USC/Information Sciences Institute, May 1987.
+
+ Contains socket numbers and mnemonics for host names,
+ operating systems, etc.
+
+[RFC-1031] W. Lazear, "MILNET Name Domain Transition", RFC-1031,
+ November 1987.
+
+ Describes a plan for converting the MILNET to the DNS.
+
+[RFC-1032] M. Stahl, "Establishing a Domain - Guidelines for
+ Administrators", RFC-1032, November 1987.
+
+
+
+Mockapetris [Page 52]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+ Describes the registration policies used by the NIC to
+ administer the top level domains and delegate subzones.
+
+[RFC-1033] M. Lottor, "Domain Administrators Operations Guide",
+ RFC-1033, November 1987.
+
+ A cookbook for domain administrators.
+
+[Solomon 82] M. Solomon, L. Landweber, and D. Neuhengen, "The CSNET
+ Name Server", Computer Networks, vol 6, nr 3, July 1982.
+
+ Describes a name service for CSNET which is independent
+ from the DNS and DNS use in the CSNET.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Mockapetris [Page 53]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+Index
+
+ * 13
+
+ ; 33, 35
+
+ <character-string> 35
+ <domain-name> 34
+
+ @ 35
+
+ \ 35
+
+ A 12
+
+ Byte order 8
+
+ CH 13
+ Character case 9
+ CLASS 11
+ CNAME 12
+ Completion 42
+ CS 13
+
+ Hesiod 13
+ HINFO 12
+ HS 13
+
+ IN 13
+ IN-ADDR.ARPA domain 22
+ Inverse queries 40
+
+ Mailbox names 47
+ MB 12
+ MD 12
+ MF 12
+ MG 12
+ MINFO 12
+ MINIMUM 20
+ MR 12
+ MX 12
+
+ NS 12
+ NULL 12
+
+ Port numbers 32
+ Primary server 5
+ PTR 12, 18
+
+
+
+Mockapetris [Page 54]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+ QCLASS 13
+ QTYPE 12
+
+ RDATA 12
+ RDLENGTH 11
+
+ Secondary server 5
+ SOA 12
+ Stub resolvers 7
+
+ TCP 32
+ TXT 12
+ TYPE 11
+
+ UDP 32
+
+ WKS 12
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Mockapetris [Page 55]
+
diff --git a/lib/dns/tests/testdata/dst/test1.dsasig b/lib/dns/tests/testdata/dst/test1.dsasig
new file mode 100644
index 0000000..5dd12e1
--- /dev/null
+++ b/lib/dns/tests/testdata/dst/test1.dsasig
@@ -0,0 +1,3 @@
+0009B55FDB62034326278C9371F32D92
+3D0E1161A32D491BEC38546FC452D903
+A91D806345B2F7F22E
diff --git a/lib/dns/tests/testdata/dst/test1.rsasig b/lib/dns/tests/testdata/dst/test1.rsasig
new file mode 100644
index 0000000..5ba62b4
--- /dev/null
+++ b/lib/dns/tests/testdata/dst/test1.rsasig
@@ -0,0 +1,5 @@
+A8A20D2F26F792B3CE76DD0E12A85DFE
+FF66AB866EF0BDB0F515001E234E699B
+F5CD6FB41FB15D4213705ABE9B563896
+2196228648E0F8AA7F2F4EED3C19165C
+1B4C70C9D69B93A1F2BE5B2F948CE023
diff --git a/lib/dns/tests/testdata/dst/test2.data b/lib/dns/tests/testdata/dst/test2.data
new file mode 100644
index 0000000..7b0e35d
--- /dev/null
+++ b/lib/dns/tests/testdata/dst/test2.data
@@ -0,0 +1,3077 @@
+Network Working Group P. Mockapetris
+Request for Comments: 1035 ISI
+ November 1987
+Obsoletes: RFCs 882, 883, 973
+
+ DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION
+
+
+1. STATUS OF THIS MEMO
+
+This RFC describes the details of the domain system and protocol, and
+assumes that the reader is familiar with the concepts discussed in a
+companion RFC, "Domain Names - Concepts and Facilities" [RFC-4301].
+
+The domain system is a mixture of functions and data types which are an
+official protocol and functions and data types which are still
+experimental. Since the domain system is intentionally extensible, new
+data types and experimental behavior should always be expected in parts
+of the system beyond the official protocol. The official protocol parts
+include standard queries, responses and the Internet class RR data
+formats (e.g., host addresses). Since the previous RFC set, several
+definitions have changed, so some previous definitions are obsolete.
+
+Experimental or obsolete features are clearly marked in these RFCs, and
+such information should be used with caution.
+
+The reader is especially cautioned not to depend on the values which
+appear in examples to be current or complete, since their purpose is
+primarily pedagogical. Distribution of this memo is unlimited.
+
+ Table of Contents
+
+ 1. STATUS OF THIS MEMO 1
+ 2. INTRODUCTION 3
+ 2.1. Overview 3
+ 2.2. Common configurations 4
+ 2.3. Conventions 7
+ 2.3.1. Preferred name syntax 7
+ 2.3.2. Data Transmission Order 8
+ 2.3.3. Character Case 9
+ 2.3.4. Size limits 10
+ 3. DOMAIN NAME SPACE AND RR DEFINITIONS 10
+ 3.1. Name space definitions 10
+ 3.2. RR definitions 11
+ 3.2.1. Format 11
+ 3.2.2. TYPE values 12
+ 3.2.3. QTYPE values 12
+ 3.2.4. CLASS values 13
+
+
+
+Mockapetris [Page 1]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+ 3.2.5. QCLASS values 13
+ 3.3. Standard RRs 13
+ 3.3.1. CNAME RDATA format 14
+ 3.3.2. HINFO RDATA format 14
+ 3.3.3. MB RDATA format (EXPERIMENTAL) 14
+ 3.3.4. MD RDATA format (Obsolete) 15
+ 3.3.5. MF RDATA format (Obsolete) 15
+ 3.3.6. MG RDATA format (EXPERIMENTAL) 16
+ 3.3.7. MINFO RDATA format (EXPERIMENTAL) 16
+ 3.3.8. MR RDATA format (EXPERIMENTAL) 17
+ 3.3.9. MX RDATA format 17
+ 3.3.10. NULL RDATA format (EXPERIMENTAL) 17
+ 3.3.11. NS RDATA format 18
+ 3.3.12. PTR RDATA format 18
+ 3.3.13. SOA RDATA format 19
+ 3.3.14. TXT RDATA format 20
+ 3.4. ARPA Internet specific RRs 20
+ 3.4.1. A RDATA format 20
+ 3.4.2. WKS RDATA format 21
+ 3.5. IN-ADDR.ARPA domain 22
+ 3.6. Defining new types, classes, and special namespaces 24
+ 4. MESSAGES 25
+ 4.1. Format 25
+ 4.1.1. Header section format 26
+ 4.1.2. Question section format 28
+ 4.1.3. Resource record format 29
+ 4.1.4. Message compression 30
+ 4.2. Transport 32
+ 4.2.1. UDP usage 32
+ 4.2.2. TCP usage 32
+ 5. MASTER FILES 33
+ 5.1. Format 33
+ 5.2. Use of master files to define zones 35
+ 5.3. Master file example 36
+ 6. NAME SERVER IMPLEMENTATION 37
+ 6.1. Architecture 37
+ 6.1.1. Control 37
+ 6.1.2. Database 37
+ 6.1.3. Time 39
+ 6.2. Standard query processing 39
+ 6.3. Zone refresh and reload processing 39
+ 6.4. Inverse queries (Optional) 40
+ 6.4.1. The contents of inverse queries and responses 40
+ 6.4.2. Inverse query and response example 41
+ 6.4.3. Inverse query processing 42
+
+
+
+
+
+
+Mockapetris [Page 2]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+ 6.5. Completion queries and responses 42
+ 7. RESOLVER IMPLEMENTATION 43
+ 7.1. Transforming a user request into a query 43
+ 7.2. Sending the queries 44
+ 7.3. Processing responses 46
+ 7.4. Using the cache 47
+ 8. MAIL SUPPORT 47
+ 8.1. Mail exchange binding 48
+ 8.2. Mailbox binding (Experimental) 48
+ 9. REFERENCES and BIBLIOGRAPHY 50
+ Index 54
+
+2. INTRODUCTION
+
+2.1. Overview
+
+The goal of domain names is to provide a mechanism for naming resources
+in such a way that the names are usable in different hosts, networks,
+protocol families, internets, and administrative organizations.
+
+From the user's point of view, domain names are useful as arguments to a
+local agent, called a resolver, which retrieves information associated
+with the domain name. Thus a user might ask for the host address or
+mail information associated with a particular domain name. To enable
+the user to request a particular type of information, an appropriate
+query type is passed to the resolver with the domain name. To the user,
+the domain tree is a single information space; the resolver is
+responsible for hiding the distribution of data among name servers from
+the user.
+
+From the resolver's point of view, the database that makes up the domain
+space is distributed among various name servers. Different parts of the
+domain space are stored in different name servers, although a particular
+data item will be stored redundantly in two or more name servers. The
+resolver starts with knowledge of at least one name server. When the
+resolver processes a user query it asks a known name server for the
+information; in return, the resolver either receives the desired
+information or a referral to another name server. Using these
+referrals, resolvers learn the identities and contents of other name
+servers. Resolvers are responsible for dealing with the distribution of
+the domain space and dealing with the effects of name server failure by
+consulting redundant databases in other servers.
+
+Name servers manage two kinds of data. The first kind of data held in
+sets called zones; each zone is the complete database for a particular
+"pruned" subtree of the domain space. This data is called
+authoritative. A name server periodically checks to make sure that its
+zones are up to date, and if not, obtains a new copy of updated zones
+
+
+
+Mockapetris [Page 3]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+from master files stored locally or in another name server. The second
+kind of data is cached data which was acquired by a local resolver.
+This data may be incomplete, but improves the performance of the
+retrieval process when non-local data is repeatedly accessed. Cached
+data is eventually discarded by a timeout mechanism.
+
+This functional structure isolates the problems of user interface,
+failure recovery, and distribution in the resolvers and isolates the
+database update and refresh problems in the name servers.
+
+2.2. Common configurations
+
+A host can participate in the domain name system in a number of ways,
+depending on whether the host runs programs that retrieve information
+from the domain system, name servers that answer queries from other
+hosts, or various combinations of both functions. The simplest, and
+perhaps most typical, configuration is shown below:
+
+ Local Host | Foreign
+ |
+ +---------+ +----------+ | +--------+
+ | | user queries | |queries | | |
+ | User |-------------->| |---------|->|Foreign |
+ | Program | | Resolver | | | Name |
+ | |<--------------| |<--------|--| Server |
+ | | user responses| |responses| | |
+ +---------+ +----------+ | +--------+
+ | A |
+ cache additions | | references |
+ V | |
+ +----------+ |
+ | cache | |
+ +----------+ |
+
+User programs interact with the domain name space through resolvers; the
+format of user queries and user responses is specific to the host and
+its operating system. User queries will typically be operating system
+calls, and the resolver and its cache will be part of the host operating
+system. Less capable hosts may choose to implement the resolver as a
+subroutine to be linked in with every program that needs its services.
+Resolvers answer user queries with information they acquire via queries
+to foreign name servers and the local cache.
+
+Note that the resolver may have to make several queries to several
+different foreign name servers to answer a particular user query, and
+hence the resolution of a user query may involve several network
+accesses and an arbitrary amount of time. The queries to foreign name
+servers and the corresponding responses have a standard format described
+
+
+
+Mockapetris [Page 4]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+in this memo, and may be datagrams.
+
+Depending on its capabilities, a name server could be a stand alone
+program on a dedicated machine or a process or processes on a large
+timeshared host. A simple configuration might be:
+
+ Local Host | Foreign
+ |
+ +---------+ |
+ / /| |
+ +---------+ | +----------+ | +--------+
+ | | | | |responses| | |
+ | | | | Name |---------|->|Foreign |
+ | Master |-------------->| Server | | |Resolver|
+ | files | | | |<--------|--| |
+ | |/ | | queries | +--------+
+ +---------+ +----------+ |
+
+Here a primary name server acquires information about one or more zones
+by reading master files from its local file system, and answers queries
+about those zones that arrive from foreign resolvers.
+
+The DNS requires that all zones be redundantly supported by more than
+one name server. Designated secondary servers can acquire zones and
+check for updates from the primary server using the zone transfer
+protocol of the DNS. This configuration is shown below:
+
+ Local Host | Foreign
+ |
+ +---------+ |
+ / /| |
+ +---------+ | +----------+ | +--------+
+ | | | | |responses| | |
+ | | | | Name |---------|->|Foreign |
+ | Master |-------------->| Server | | |Resolver|
+ | files | | | |<--------|--| |
+ | |/ | | queries | +--------+
+ +---------+ +----------+ |
+ A |maintenance | +--------+
+ | +------------|->| |
+ | queries | |Foreign |
+ | | | Name |
+ +------------------|--| Server |
+ maintenance responses | +--------+
+
+In this configuration, the name server periodically establishes a
+virtual circuit to a foreign name server to acquire a copy of a zone or
+to check that an existing copy has not changed. The messages sent for
+
+
+
+Mockapetris [Page 5]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+these maintenance activities follow the same form as queries and
+responses, but the message sequences are somewhat different.
+
+The information flow in a host that supports all aspects of the domain
+name system is shown below:
+
+ Local Host | Foreign
+ |
+ +---------+ +----------+ | +--------+
+ | | user queries | |queries | | |
+ | User |-------------->| |---------|->|Foreign |
+ | Program | | Resolver | | | Name |
+ | |<--------------| |<--------|--| Server |
+ | | user responses| |responses| | |
+ +---------+ +----------+ | +--------+
+ | A |
+ cache additions | | references |
+ V | |
+ +----------+ |
+ | Shared | |
+ | database | |
+ +----------+ |
+ A | |
+ +---------+ refreshes | | references |
+ / /| | V |
+ +---------+ | +----------+ | +--------+
+ | | | | |responses| | |
+ | | | | Name |---------|->|Foreign |
+ | Master |-------------->| Server | | |Resolver|
+ | files | | | |<--------|--| |
+ | |/ | | queries | +--------+
+ +---------+ +----------+ |
+ A |maintenance | +--------+
+ | +------------|->| |
+ | queries | |Foreign |
+ | | | Name |
+ +------------------|--| Server |
+ maintenance responses | +--------+
+
+The shared database holds domain space data for the local name server
+and resolver. The contents of the shared database will typically be a
+mixture of authoritative data maintained by the periodic refresh
+operations of the name server and cached data from previous resolver
+requests. The structure of the domain data and the necessity for
+synchronization between name servers and resolvers imply the general
+characteristics of this database, but the actual format is up to the
+local implementor.
+
+
+
+
+Mockapetris [Page 6]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+Information flow can also be tailored so that a group of hosts act
+together to optimize activities. Sometimes this is done to offload less
+capable hosts so that they do not have to implement a full resolver.
+This can be appropriate for PCs or hosts which want to minimize the
+amount of new network code which is required. This scheme can also
+allow a group of hosts can share a small number of caches rather than
+maintaining a large number of separate caches, on the premise that the
+centralized caches will have a higher hit ratio. In either case,
+resolvers are replaced with stub resolvers which act as front ends to
+resolvers located in a recursive server in one or more name servers
+known to perform that service:
+
+ Local Hosts | Foreign
+ |
+ +---------+ |
+ | | responses |
+ | Stub |<--------------------+ |
+ | Resolver| | |
+ | |----------------+ | |
+ +---------+ recursive | | |
+ queries | | |
+ V | |
+ +---------+ recursive +----------+ | +--------+
+ | | queries | |queries | | |
+ | Stub |-------------->| Recursive|---------|->|Foreign |
+ | Resolver| | Server | | | Name |
+ | |<--------------| |<--------|--| Server |
+ +---------+ responses | |responses| | |
+ +----------+ | +--------+
+ | Central | |
+ | cache | |
+ +----------+ |
+
+In any case, note that domain components are always replicated for
+reliability whenever possible.
+
+2.3. Conventions
+
+The domain system has several conventions dealing with low-level, but
+fundamental, issues. While the implementor is free to violate these
+conventions WITHIN HIS OWN SYSTEM, he must observe these conventions in
+ALL behavior observed from other hosts.
+
+2.3.1. Preferred name syntax
+
+The DNS specifications attempt to be as general as possible in the rules
+for constructing domain names. The idea is that the name of any
+existing object can be expressed as a domain name with minimal changes.
+
+
+
+Mockapetris [Page 7]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+However, when assigning a domain name for an object, the prudent user
+will select a name which satisfies both the rules of the domain system
+and any existing rules for the object, whether these rules are published
+or implied by existing programs.
+
+For example, when naming a mail domain, the user should satisfy both the
+rules of this memo and those in RFC-822. When creating a new host name,
+the old rules for HOSTS.TXT should be followed. This avoids problems
+when old software is converted to use domain names.
+
+The following syntax will result in fewer problems with many
+
+applications that use domain names (e.g., mail, TELNET).
+
+<domain> ::= <subdomain> | " "
+
+<subdomain> ::= <label> | <subdomain> "." <label>
+
+<label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
+
+<ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
+
+<let-dig-hyp> ::= <let-dig> | "-"
+
+<let-dig> ::= <letter> | <digit>
+
+<letter> ::= any one of the 52 alphabetic characters A through Z in
+upper case and a through z in lower case
+
+<digit> ::= any one of the ten digits 0 through 9
+
+Note that while upper and lower case letters are allowed in domain
+names, no significance is attached to the case. That is, two names with
+the same spelling but different case are to be treated as if identical.
+
+The labels must follow the rules for ARPANET host names. They must
+start with a letter, end with a letter or digit, and have as interior
+characters only letters, digits, and hyphen. There are also some
+restrictions on the length. Labels must be 63 characters or less.
+
+For example, the following strings identify hosts in the Internet:
+
+A.ISI.EDU XX.LCS.MIT.EDU SRI-NIC.ARPA
+
+2.3.2. Data Transmission Order
+
+The order of transmission of the header and data described in this
+document is resolved to the octet level. Whenever a diagram shows a
+
+
+
+Mockapetris [Page 8]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+group of octets, the order of transmission of those octets is the normal
+order in which they are read in English. For example, in the following
+diagram, the octets are transmitted in the order they are numbered.
+
+ 0 1
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | 1 | 2 |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | 3 | 4 |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | 5 | 6 |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+Whenever an octet represents a numeric quantity, the left most bit in
+the diagram is the high order or most significant bit. That is, the bit
+labeled 0 is the most significant bit. For example, the following
+diagram represents the value 170 (decimal).
+
+ 0 1 2 3 4 5 6 7
+ +-+-+-+-+-+-+-+-+
+ |1 0 1 0 1 0 1 0|
+ +-+-+-+-+-+-+-+-+
+
+Similarly, whenever a multi-octet field represents a numeric quantity
+the left most bit of the whole field is the most significant bit. When
+a multi-octet quantity is transmitted the most significant octet is
+transmitted first.
+
+2.3.3. Character Case
+
+For all parts of the DNS that are part of the official protocol, all
+comparisons between character strings (e.g., labels, domain names, etc.)
+are done in a case-insensitive manner. At present, this rule is in
+force throughout the domain system without exception. However, future
+additions beyond current usage may need to use the full binary octet
+capabilities in names, so attempts to store domain names in 7-bit ASCII
+or use of special bytes to terminate labels, etc., should be avoided.
+
+When data enters the domain system, its original case should be
+preserved whenever possible. In certain circumstances this cannot be
+done. For example, if two RRs are stored in a database, one at x.y and
+one at X.Y, they are actually stored at the same place in the database,
+and hence only one casing would be preserved. The basic rule is that
+case can be discarded only when data is used to define structure in a
+database, and two names are identical when compared in a case
+insensitive manner.
+
+
+
+
+Mockapetris [Page 9]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+Loss of case sensitive data must be minimized. Thus while data for x.y
+and X.Y may both be stored under a single location x.y or X.Y, data for
+a.x and B.X would never be stored under A.x, A.X, b.x, or b.X. In
+general, this preserves the case of the first label of a domain name,
+but forces standardization of interior node labels.
+
+Systems administrators who enter data into the domain database should
+take care to represent the data they supply to the domain system in a
+case-consistent manner if their system is case-sensitive. The data
+distribution system in the domain system will ensure that consistent
+representations are preserved.
+
+2.3.4. Size limits
+
+Various objects and parameters in the DNS have size limits. They are
+listed below. Some could be easily changed, others are more
+fundamental.
+
+labels 63 octets or less
+
+names 255 octets or less
+
+TTL positive values of a signed 32 bit number.
+
+UDP messages 512 octets or less
+
+3. DOMAIN NAME SPACE AND RR DEFINITIONS
+
+3.1. Name space definitions
+
+Domain names in messages are expressed in terms of a sequence of labels.
+Each label is represented as a one octet length field followed by that
+number of octets. Since every domain name ends with the null label of
+the root, a domain name is terminated by a length byte of zero. The
+high order two bits of every length octet must be zero, and the
+remaining six bits of the length field limit the label to 63 octets or
+less.
+
+To simplify implementations, the total length of a domain name (i.e.,
+label octets and label length octets) is restricted to 255 octets or
+less.
+
+Although labels can contain any 8 bit values in octets that make up a
+label, it is strongly recommended that labels follow the preferred
+syntax described elsewhere in this memo, which is compatible with
+existing host naming conventions. Name servers and resolvers must
+compare labels in a case-insensitive manner (i.e., A=a), assuming ASCII
+with zero parity. Non-alphabetic codes must match exactly.
+
+
+
+Mockapetris [Page 10]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+3.2. RR definitions
+
+3.2.1. Format
+
+All RRs have the same top level format shown below:
+
+ 1 1 1 1 1 1
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | |
+ / /
+ / NAME /
+ | |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | TYPE |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | CLASS |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | TTL |
+ | |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | RDLENGTH |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
+ / RDATA /
+ / /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+
+where:
+
+NAME an owner name, i.e., the name of the node to which this
+ resource record pertains.
+
+TYPE two octets containing one of the RR TYPE codes.
+
+CLASS two octets containing one of the RR CLASS codes.
+
+TTL a 32 bit signed integer that specifies the time interval
+ that the resource record may be cached before the source
+ of the information should again be consulted. Zero
+ values are interpreted to mean that the RR can only be
+ used for the transaction in progress, and should not be
+ cached. For example, SOA records are always distributed
+ with a zero TTL to prohibit caching. Zero values can
+ also be used for extremely volatile data.
+
+RDLENGTH an unsigned 16 bit integer that specifies the length in
+ octets of the RDATA field.
+
+
+
+Mockapetris [Page 11]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+RDATA a variable length string of octets that describes the
+ resource. The format of this information varies
+ according to the TYPE and CLASS of the resource record.
+
+3.2.2. TYPE values
+
+TYPE fields are used in resource records. Note that these types are a
+subset of QTYPEs.
+
+TYPE value and meaning
+
+A 1 a host address
+
+NS 2 an authoritative name server
+
+MD 3 a mail destination (Obsolete - use MX)
+
+MF 4 a mail forwarder (Obsolete - use MX)
+
+CNAME 5 the canonical name for an alias
+
+SOA 6 marks the start of a zone of authority
+
+MB 7 a mailbox domain name (EXPERIMENTAL)
+
+MG 8 a mail group member (EXPERIMENTAL)
+
+MR 9 a mail rename domain name (EXPERIMENTAL)
+
+NULL 10 a null RR (EXPERIMENTAL)
+
+WKS 11 a well known service description
+
+PTR 12 a domain name pointer
+
+HINFO 13 host information
+
+MINFO 14 mailbox or mail list information
+
+MX 15 mail exchange
+
+TXT 16 text strings
+
+3.2.3. QTYPE values
+
+QTYPE fields appear in the question part of a query. QTYPES are a
+superset of TYPEs, hence all TYPEs are valid QTYPEs. In addition, the
+following QTYPEs are defined:
+
+
+
+Mockapetris [Page 12]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+AXFR 252 A request for a transfer of an entire zone
+
+MAILB 253 A request for mailbox-related records (MB, MG or MR)
+
+MAILA 254 A request for mail agent RRs (Obsolete - see MX)
+
+* 255 A request for all records
+
+3.2.4. CLASS values
+
+CLASS fields appear in resource records. The following CLASS mnemonics
+and values are defined:
+
+IN 1 the Internet
+
+CS 2 the CSNET class (Obsolete - used only for examples in
+ some obsolete RFCs)
+
+CH 3 the CHAOS class
+
+HS 4 Hesiod [Dyer 87]
+
+3.2.5. QCLASS values
+
+QCLASS fields appear in the question section of a query. QCLASS values
+are a superset of CLASS values; every CLASS is a valid QCLASS. In
+addition to CLASS values, the following QCLASSes are defined:
+
+* 255 any class
+
+3.3. Standard RRs
+
+The following RR definitions are expected to occur, at least
+potentially, in all classes. In particular, NS, SOA, CNAME, and PTR
+will be used in all classes, and have the same format in all classes.
+Because their RDATA format is known, all domain names in the RDATA
+section of these RRs may be compressed.
+
+<domain-name> is a domain name represented as a series of labels, and
+terminated by a label with zero length. <character-string> is a single
+length octet followed by that number of characters. <character-string>
+is treated as binary information, and can be up to 256 characters in
+length (including the length octet).
+
+
+
+
+
+
+
+
+Mockapetris [Page 13]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+3.3.1. CNAME RDATA format
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ / CNAME /
+ / /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+CNAME A <domain-name> which specifies the canonical or primary
+ name for the owner. The owner name is an alias.
+
+CNAME RRs cause no additional section processing, but name servers may
+choose to restart the query at the canonical name in certain cases. See
+the description of name server logic in [RFC-1034] for details.
+
+3.3.2. HINFO RDATA format
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ / CPU /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ / OS /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+CPU A <character-string> which specifies the CPU type.
+
+OS A <character-string> which specifies the operating
+ system type.
+
+Standard values for CPU and OS can be found in [RFC-1010].
+
+HINFO records are used to acquire general information about a host. The
+main use is for protocols such as FTP that can use special procedures
+when talking between machines or operating systems of the same type.
+
+3.3.3. MB RDATA format (EXPERIMENTAL)
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ / MADNAME /
+ / /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+MADNAME A <domain-name> which specifies a host which has the
+ specified mailbox.
+
+
+
+Mockapetris [Page 14]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+MB records cause additional section processing which looks up an A type
+RRs corresponding to MADNAME.
+
+3.3.4. MD RDATA format (Obsolete)
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ / MADNAME /
+ / /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+MADNAME A <domain-name> which specifies a host which has a mail
+ agent for the domain which should be able to deliver
+ mail for the domain.
+
+MD records cause additional section processing which looks up an A type
+record corresponding to MADNAME.
+
+MD is obsolete. See the definition of MX and [RFC-974] for details of
+the new scheme. The recommended policy for dealing with MD RRs found in
+a master file is to reject them, or to convert them to MX RRs with a
+preference of 0.
+
+3.3.5. MF RDATA format (Obsolete)
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ / MADNAME /
+ / /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+MADNAME A <domain-name> which specifies a host which has a mail
+ agent for the domain which will accept mail for
+ forwarding to the domain.
+
+MF records cause additional section processing which looks up an A type
+record corresponding to MADNAME.
+
+MF is obsolete. See the definition of MX and [RFC-974] for details ofw
+the new scheme. The recommended policy for dealing with MD RRs found in
+a master file is to reject them, or to convert them to MX RRs with a
+preference of 10.
+
+
+
+
+
+
+
+Mockapetris [Page 15]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+3.3.6. MG RDATA format (EXPERIMENTAL)
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ / MGMNAME /
+ / /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+MGMNAME A <domain-name> which specifies a mailbox which is a
+ member of the mail group specified by the domain name.
+
+MG records cause no additional section processing.
+
+3.3.7. MINFO RDATA format (EXPERIMENTAL)
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ / RMAILBX /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ / EMAILBX /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+RMAILBX A <domain-name> which specifies a mailbox which is
+ responsible for the mailing list or mailbox. If this
+ domain name names the root, the owner of the MINFO RR is
+ responsible for itself. Note that many existing mailing
+ lists use a mailbox X-request for the RMAILBX field of
+ mailing list X, e.g., Msgroup-request for Msgroup. This
+ field provides a more general mechanism.
+
+
+EMAILBX A <domain-name> which specifies a mailbox which is to
+ receive error messages related to the mailing list or
+ mailbox specified by the owner of the MINFO RR (similar
+ to the ERRORS-TO: field which has been proposed). If
+ this domain name names the root, errors should be
+ returned to the sender of the message.
+
+MINFO records cause no additional section processing. Although these
+records can be associated with a simple mailbox, they are usually used
+with a mailing list.
+
+
+
+
+
+
+
+
+Mockapetris [Page 16]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+3.3.8. MR RDATA format (EXPERIMENTAL)
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ / NEWNAME /
+ / /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+NEWNAME A <domain-name> which specifies a mailbox which is the
+ proper rename of the specified mailbox.
+
+MR records cause no additional section processing. The main use for MR
+is as a forwarding entry for a user who has moved to a different
+mailbox.
+
+3.3.9. MX RDATA format
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | PREFERENCE |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ / EXCHANGE /
+ / /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+PREFERENCE A 16 bit integer which specifies the preference given to
+ this RR among others at the same owner. Lower values
+ are preferred.
+
+EXCHANGE A <domain-name> which specifies a host willing to act as
+ a mail exchange for the owner name.
+
+MX records cause type A additional section processing for the host
+specified by EXCHANGE. The use of MX RRs is explained in detail in
+[RFC-974].
+
+3.3.10. NULL RDATA format (EXPERIMENTAL)
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ / <anything> /
+ / /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+Anything at all may be in the RDATA field so long as it is 65535 octets
+or less.
+
+
+
+
+Mockapetris [Page 17]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+NULL records cause no additional section processing. NULL RRs are not
+allowed in master files. NULLs are used as placeholders in some
+experimental extensions of the DNS.
+
+3.3.11. NS RDATA format
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ / NSDNAME /
+ / /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+NSDNAME A <domain-name> which specifies a host which should be
+ authoritative for the specified class and domain.
+
+NS records cause both the usual additional section processing to locate
+a type A record, and, when used in a referral, a special search of the
+zone in which they reside for glue information.
+
+The NS RR states that the named host should be expected to have a zone
+starting at owner name of the specified class. Note that the class may
+not indicate the protocol family which should be used to communicate
+with the host, although it is typically a strong hint. For example,
+hosts which are name servers for either Internet (IN) or Hesiod (HS)
+class information are normally queried using IN class protocols.
+
+3.3.12. PTR RDATA format
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ / PTRDNAME /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+PTRDNAME A <domain-name> which points to some location in the
+ domain name space.
+
+PTR records cause no additional section processing. These RRs are used
+in special domains to point to some other location in the domain space.
+These records are simple data, and don't imply any special processing
+similar to that performed by CNAME, which identifies aliases. See the
+description of the IN-ADDR.ARPA domain for an example.
+
+
+
+
+
+
+
+
+Mockapetris [Page 18]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+3.3.13. SOA RDATA format
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ / MNAME /
+ / /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ / RNAME /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | SERIAL |
+ | |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | REFRESH |
+ | |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | RETRY |
+ | |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | EXPIRE |
+ | |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | MINIMUM |
+ | |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+MNAME The <domain-name> of the name server that was the
+ original or primary source of data for this zone.
+
+RNAME A <domain-name> which specifies the mailbox of the
+ person responsible for this zone.
+
+SERIAL The unsigned 32 bit version number of the original copy
+ of the zone. Zone transfers preserve this value. This
+ value wraps and should be compared using sequence space
+ arithmetic.
+
+REFRESH A 32 bit time interval before the zone should be
+ refreshed.
+
+RETRY A 32 bit time interval that should elapse before a
+ failed refresh should be retried.
+
+EXPIRE A 32 bit time value that specifies the upper limit on
+ the time interval that can elapse before the zone is no
+ longer authoritative.
+
+
+
+
+
+Mockapetris [Page 19]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+MINIMUM The unsigned 32 bit minimum TTL field that should be
+ exported with any RR from this zone.
+
+SOA records cause no additional section processing.
+
+All times are in units of seconds.
+
+Most of these fields are pertinent only for name server maintenance
+operations. However, MINIMUM is used in all query operations that
+retrieve RRs from a zone. Whenever a RR is sent in a response to a
+query, the TTL field is set to the maximum of the TTL field from the RR
+and the MINIMUM field in the appropriate SOA. Thus MINIMUM is a lower
+bound on the TTL field for all RRs in a zone. Note that this use of
+MINIMUM should occur when the RRs are copied into the response and not
+when the zone is loaded from a master file or via a zone transfer. The
+reason for this provison is to allow future dynamic update facilities to
+change the SOA RR with known semantics.
+
+
+3.3.14. TXT RDATA format
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ / TXT-DATA /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+TXT-DATA One or more <character-string>s.
+
+TXT RRs are used to hold descriptive text. The semantics of the text
+depends on the domain where it is found.
+
+3.4. Internet specific RRs
+
+3.4.1. A RDATA format
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | ADDRESS |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+ADDRESS A 32 bit Internet address.
+
+Hosts that have multiple Internet addresses will have multiple A
+records.
+
+
+
+
+
+Mockapetris [Page 20]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+A records cause no additional section processing. The RDATA section of
+an A line in a master file is an Internet address expressed as four
+decimal numbers separated by dots without any imbedded spaces (e.g.,
+"10.2.0.52" or "192.0.5.6").
+
+3.4.2. WKS RDATA format
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | ADDRESS |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | PROTOCOL | |
+ +--+--+--+--+--+--+--+--+ |
+ | |
+ / <BIT MAP> /
+ / /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+ADDRESS An 32 bit Internet address
+
+PROTOCOL An 8 bit IP protocol number
+
+<BIT MAP> A variable length bit map. The bit map must be a
+ multiple of 8 bits long.
+
+The WKS record is used to describe the well known services supported by
+a particular protocol on a particular internet address. The PROTOCOL
+field specifies an IP protocol number, and the bit map has one bit per
+port of the specified protocol. The first bit corresponds to port 0,
+the second to port 1, etc. If the bit map does not include a bit for a
+protocol of interest, that bit is assumed zero. The appropriate values
+and mnemonics for ports and protocols are specified in [RFC-1010].
+
+For example, if PROTOCOL=TCP (6), the 26th bit corresponds to TCP port
+25 (SMTP). If this bit is set, a SMTP server should be listening on TCP
+port 25; if zero, SMTP service is not supported on the specified
+address.
+
+The purpose of WKS RRs is to provide availability information for
+servers for TCP and UDP. If a server supports both TCP and UDP, or has
+multiple Internet addresses, then multiple WKS RRs are used.
+
+WKS RRs cause no additional section processing.
+
+In master files, both ports and protocols are expressed using mnemonics
+or decimal numbers.
+
+
+
+
+Mockapetris [Page 21]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+3.5. IN-ADDR.ARPA domain
+
+The Internet uses a special domain to support gateway location and
+Internet address to host mapping. Other classes may employ a similar
+strategy in other domains. The intent of this domain is to provide a
+guaranteed method to perform host address to host name mapping, and to
+facilitate queries to locate all gateways on a particular network in the
+Internet.
+
+Note that both of these services are similar to functions that could be
+performed by inverse queries; the difference is that this part of the
+domain name space is structured according to address, and hence can
+guarantee that the appropriate data can be located without an exhaustive
+search of the domain space.
+
+The domain begins at IN-ADDR.ARPA and has a substructure which follows
+the Internet addressing structure.
+
+Domain names in the IN-ADDR.ARPA domain are defined to have up to four
+labels in addition to the IN-ADDR.ARPA suffix. Each label represents
+one octet of an Internet address, and is expressed as a character string
+for a decimal value in the range 0-255 (with leading zeros omitted
+except in the case of a zero octet which is represented by a single
+zero).
+
+Host addresses are represented by domain names that have all four labels
+specified. Thus data for Internet address 10.2.0.52 is located at
+domain name 52.0.2.10.IN-ADDR.ARPA. The reversal, though awkward to
+read, allows zones to be delegated which are exactly one network of
+address space. For example, 10.IN-ADDR.ARPA can be a zone containing
+data for the ARPANET, while 26.IN-ADDR.ARPA can be a separate zone for
+MILNET. Address nodes are used to hold pointers to primary host names
+in the normal domain space.
+
+Network numbers correspond to some non-terminal nodes at various depths
+in the IN-ADDR.ARPA domain, since Internet network numbers are either 1,
+2, or 3 octets. Network nodes are used to hold pointers to the primary
+host names of gateways attached to that network. Since a gateway is, by
+definition, on more than one network, it will typically have two or more
+network nodes which point at it. Gateways will also have host level
+pointers at their fully qualified addresses.
+
+Both the gateway pointers at network nodes and the normal host pointers
+at full address nodes use the PTR RR to point back to the primary domain
+names of the corresponding hosts.
+
+For example, the IN-ADDR.ARPA domain will contain information about the
+ISI gateway between net 10 and 26, an MIT gateway from net 10 to MIT's
+
+
+
+Mockapetris [Page 22]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+net 18, and hosts A.ISI.EDU and MULTICS.MIT.EDU. Assuming that ISI
+gateway has addresses 10.2.0.22 and 26.0.0.103, and a name MILNET-
+GW.ISI.EDU, and the MIT gateway has addresses 10.0.0.77 and 18.10.0.4
+and a name GW.LCS.MIT.EDU, the domain database would contain:
+
+ 10.IN-ADDR.ARPA. PTR MILNET-GW.ISI.EDU.
+ 10.IN-ADDR.ARPA. PTR GW.LCS.MIT.EDU.
+ 18.IN-ADDR.ARPA. PTR GW.LCS.MIT.EDU.
+ 26.IN-ADDR.ARPA. PTR MILNET-GW.ISI.EDU.
+ 22.0.2.10.IN-ADDR.ARPA. PTR MILNET-GW.ISI.EDU.
+ 103.0.0.26.IN-ADDR.ARPA. PTR MILNET-GW.ISI.EDU.
+ 77.0.0.10.IN-ADDR.ARPA. PTR GW.LCS.MIT.EDU.
+ 4.0.10.18.IN-ADDR.ARPA. PTR GW.LCS.MIT.EDU.
+ 103.0.3.26.IN-ADDR.ARPA. PTR A.ISI.EDU.
+ 6.0.0.10.IN-ADDR.ARPA. PTR MULTICS.MIT.EDU.
+
+Thus a program which wanted to locate gateways on net 10 would originate
+a query of the form QTYPE=PTR, QCLASS=IN, QNAME=10.IN-ADDR.ARPA. It
+would receive two RRs in response:
+
+ 10.IN-ADDR.ARPA. PTR MILNET-GW.ISI.EDU.
+ 10.IN-ADDR.ARPA. PTR GW.LCS.MIT.EDU.
+
+The program could then originate QTYPE=A, QCLASS=IN queries for MILNET-
+GW.ISI.EDU. and GW.LCS.MIT.EDU. to discover the Internet addresses of
+these gateways.
+
+A resolver which wanted to find the host name corresponding to Internet
+host address 10.0.0.6 would pursue a query of the form QTYPE=PTR,
+QCLASS=IN, QNAME=6.0.0.10.IN-ADDR.ARPA, and would receive:
+
+ 6.0.0.10.IN-ADDR.ARPA. PTR MULTICS.MIT.EDU.
+
+Several cautions apply to the use of these services:
+ - Since the IN-ADDR.ARPA special domain and the normal domain
+ for a particular host or gateway will be in different zones,
+ the possibility exists that that the data may be inconsistent.
+
+ - Gateways will often have two names in separate domains, only
+ one of which can be primary.
+
+ - Systems that use the domain database to initialize their
+ routing tables must start with enough gateway information to
+ guarantee that they can access the appropriate name server.
+
+ - The gateway data only reflects the existence of a gateway in a
+ manner equivalent to the current HOSTS.TXT file. It doesn't
+ replace the dynamic availability information from GGP or EGP.
+
+
+
+Mockapetris [Page 23]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+3.6. Defining new types, classes, and special namespaces
+
+The previously defined types and classes are the ones in use as of the
+date of this memo. New definitions should be expected. This section
+makes some recommendations to designers considering additions to the
+existing facilities. The mailing list NAMEDROPPERS@SRI-NIC.ARPA is the
+forum where general discussion of design issues takes place.
+
+In general, a new type is appropriate when new information is to be
+added to the database about an existing object, or we need new data
+formats for some totally new object. Designers should attempt to define
+types and their RDATA formats that are generally applicable to all
+classes, and which avoid duplication of information. New classes are
+appropriate when the DNS is to be used for a new protocol, etc which
+requires new class-specific data formats, or when a copy of the existing
+name space is desired, but a separate management domain is necessary.
+
+New types and classes need mnemonics for master files; the format of the
+master files requires that the mnemonics for type and class be disjoint.
+
+TYPE and CLASS values must be a proper subset of QTYPEs and QCLASSes
+respectively.
+
+The present system uses multiple RRs to represent multiple values of a
+type rather than storing multiple values in the RDATA section of a
+single RR. This is less efficient for most applications, but does keep
+RRs shorter. The multiple RRs assumption is incorporated in some
+experimental work on dynamic update methods.
+
+The present system attempts to minimize the duplication of data in the
+database in order to insure consistency. Thus, in order to find the
+address of the host for a mail exchange, you map the mail domain name to
+a host name, then the host name to addresses, rather than a direct
+mapping to host address. This approach is preferred because it avoids
+the opportunity for inconsistency.
+
+In defining a new type of data, multiple RR types should not be used to
+create an ordering between entries or express different formats for
+equivalent bindings, instead this information should be carried in the
+body of the RR and a single type used. This policy avoids problems with
+caching multiple types and defining QTYPEs to match multiple types.
+
+For example, the original form of mail exchange binding used two RR
+types one to represent a "closer" exchange (MD) and one to represent a
+"less close" exchange (MF). The difficulty is that the presence of one
+RR type in a cache doesn't convey any information about the other
+because the query which acquired the cached information might have used
+a QTYPE of MF, MD, or MAILA (which matched both). The redesigned
+
+
+
+Mockapetris [Page 24]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+service used a single type (MX) with a "preference" value in the RDATA
+section which can order different RRs. However, if any MX RRs are found
+in the cache, then all should be there.
+
+4. MESSAGES
+
+4.1. Format
+
+All communications inside of the domain protocol are carried in a single
+format called a message. The top level format of message is divided
+into 5 sections (some of which are empty in certain cases) shown below:
+
+ +---------------------+
+ | Header |
+ +---------------------+
+ | Question | the question for the name server
+ +---------------------+
+ | Answer | RRs answering the question
+ +---------------------+
+ | Authority | RRs pointing toward an authority
+ +---------------------+
+ | Additional | RRs holding additional information
+ +---------------------+
+
+The header section is always present. The header includes fields that
+specify which of the remaining sections are present, and also specify
+whether the message is a query or a response, a standard query or some
+other opcode, etc.
+
+The names of the sections after the header are derived from their use in
+standard queries. The question section contains fields that describe a
+question to a name server. These fields are a query type (QTYPE), a
+query class (QCLASS), and a query domain name (QNAME). The last three
+sections have the same format: a possibly empty list of concatenated
+resource records (RRs). The answer section contains RRs that answer the
+question; the authority section contains RRs that point toward an
+authoritative name server; the additional records section contains RRs
+which relate to the query, but are not strictly answers for the
+question.
+
+
+
+
+
+
+
+
+
+
+
+
+Mockapetris [Page 25]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+4.1.1. Header section format
+
+The header contains the following fields:
+
+ 1 1 1 1 1 1
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | ID |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ |QR| Opcode |AA|TC|RD|RA| Z | RCODE |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | QDCOUNT |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | ANCOUNT |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | NSCOUNT |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | ARCOUNT |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+ID A 16 bit identifier assigned by the program that
+ generates any kind of query. This identifier is copied
+ the corresponding reply and can be used by the requester
+ to match up replies to outstanding queries.
+
+QR A one bit field that specifies whether this message is a
+ query (0), or a response (1).
+
+OPCODE A four bit field that specifies kind of query in this
+ message. This value is set by the originator of a query
+ and copied into the response. The values are:
+
+ 0 a standard query (QUERY)
+
+ 1 an inverse query (IQUERY)
+
+ 2 a server status request (STATUS)
+
+ 3-15 reserved for future use
+
+AA Authoritative Answer - this bit is valid in responses,
+ and specifies that the responding name server is an
+ authority for the domain name in question section.
+
+ Note that the contents of the answer section may have
+ multiple owner names because of aliases. The AA bit
+
+
+
+Mockapetris [Page 26]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+ corresponds to the name which matches the query name, or
+ the first owner name in the answer section.
+
+TC TrunCation - specifies that this message was truncated
+ due to length greater than that permitted on the
+ transmission channel.
+
+RD Recursion Desired - this bit may be set in a query and
+ is copied into the response. If RD is set, it directs
+ the name server to pursue the query recursively.
+ Recursive query support is optional.
+
+RA Recursion Available - this be is set or cleared in a
+ response, and denotes whether recursive query support is
+ available in the name server.
+
+Z Reserved for future use. Must be zero in all queries
+ and responses.
+
+RCODE Response code - this 4 bit field is set as part of
+ responses. The values have the following
+ interpretation:
+
+ 0 No error condition
+
+ 1 Format error - The name server was
+ unable to interpret the query.
+
+ 2 Server failure - The name server was
+ unable to process this query due to a
+ problem with the name server.
+
+ 3 Name Error - Meaningful only for
+ responses from an authoritative name
+ server, this code signifies that the
+ domain name referenced in the query does
+ not exist.
+
+ 4 Not Implemented - The name server does
+ not support the requested kind of query.
+
+ 5 Refused - The name server refuses to
+ perform the specified operation for
+ policy reasons. For example, a name
+ server may not wish to provide the
+ information to the particular requester,
+ or a name server may not wish to perform
+ a particular operation (e.g., zone
+
+
+
+Mockapetris [Page 27]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+ transfer) for particular data.
+
+ 6-15 Reserved for future use.
+
+QDCOUNT an unsigned 16 bit integer specifying the number of
+ entries in the question section.
+
+ANCOUNT an unsigned 16 bit integer specifying the number of
+ resource records in the answer section.
+
+NSCOUNT an unsigned 16 bit integer specifying the number of name
+ server resource records in the authority records
+ section.
+
+ARCOUNT an unsigned 16 bit integer specifying the number of
+ resource records in the additional records section.
+
+4.1.2. Question section format
+
+The question section is used to carry the "question" in most queries,
+i.e., the parameters that define what is being asked. The section
+contains QDCOUNT (usually 1) entries, each of the following format:
+
+ 1 1 1 1 1 1
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | |
+ / QNAME /
+ / /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | QTYPE |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | QCLASS |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+QNAME a domain name represented as a sequence of labels, where
+ each label consists of a length octet followed by that
+ number of octets. The domain name terminates with the
+ zero length octet for the null label of the root. Note
+ that this field may be an odd number of octets; no
+ padding is used.
+
+QTYPE a two octet code which specifies the type of the query.
+ The values for this field include all codes valid for a
+ TYPE field, together with some more general codes which
+ can match more than one type of RR.
+
+
+
+Mockapetris [Page 28]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+QCLASS a two octet code that specifies the class of the query.
+ For example, the QCLASS field is IN for the Internet.
+
+4.1.3. Resource record format
+
+The answer, authority, and additional sections all share the same
+format: a variable number of resource records, where the number of
+records is specified in the corresponding count field in the header.
+Each resource record has the following format:
+ 1 1 1 1 1 1
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | |
+ / /
+ / NAME /
+ | |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | TYPE |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | CLASS |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | TTL |
+ | |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | RDLENGTH |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
+ / RDATA /
+ / /
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+NAME a domain name to which this resource record pertains.
+
+TYPE two octets containing one of the RR type codes. This
+ field specifies the meaning of the data in the RDATA
+ field.
+
+CLASS two octets which specify the class of the data in the
+ RDATA field.
+
+TTL a 32 bit unsigned integer that specifies the time
+ interval (in seconds) that the resource record may be
+ cached before it should be discarded. Zero values are
+ interpreted to mean that the RR can only be used for the
+ transaction in progress, and should not be cached.
+
+
+
+
+
+Mockapetris [Page 29]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+RDLENGTH an unsigned 16 bit integer that specifies the length in
+ octets of the RDATA field.
+
+RDATA a variable length string of octets that describes the
+ resource. The format of this information varies
+ according to the TYPE and CLASS of the resource record.
+ For example, the if the TYPE is A and the CLASS is IN,
+ the RDATA field is a 4 octet ARPA Internet address.
+
+4.1.4. Message compression
+
+In order to reduce the size of messages, the domain system utilizes a
+compression scheme which eliminates the repetition of domain names in a
+message. In this scheme, an entire domain name or a list of labels at
+the end of a domain name is replaced with a pointer to a prior occurance
+of the same name.
+
+The pointer takes the form of a two octet sequence:
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | 1 1| OFFSET |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+The first two bits are ones. This allows a pointer to be distinguished
+from a label, since the label must begin with two zero bits because
+labels are restricted to 63 octets or less. (The 10 and 01 combinations
+are reserved for future use.) The OFFSET field specifies an offset from
+the start of the message (i.e., the first octet of the ID field in the
+domain header). A zero offset specifies the first byte of the ID field,
+etc.
+
+The compression scheme allows a domain name in a message to be
+represented as either:
+
+ - a sequence of labels ending in a zero octet
+
+ - a pointer
+
+ - a sequence of labels ending with a pointer
+
+Pointers can only be used for occurances of a domain name where the
+format is not class specific. If this were not the case, a name server
+or resolver would be required to know the format of all RRs it handled.
+As yet, there are no such cases, but they may occur in future RDATA
+formats.
+
+If a domain name is contained in a part of the message subject to a
+length field (such as the RDATA section of an RR), and compression is
+
+
+
+Mockapetris [Page 30]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+used, the length of the compressed name is used in the length
+calculation, rather than the length of the expanded name.
+
+Programs are free to avoid using pointers in messages they generate,
+although this will reduce datagram capacity, and may cause truncation.
+However all programs are required to understand arriving messages that
+contain pointers.
+
+For example, a datagram might need to use the domain names F.ISI.ARPA,
+FOO.F.ISI.ARPA, ARPA, and the root. Ignoring the other fields of the
+message, these domain names might be represented as:
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ 20 | 1 | F |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ 22 | 3 | I |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ 24 | S | I |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ 26 | 4 | A |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ 28 | R | P |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ 30 | A | 0 |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ 40 | 3 | F |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ 42 | O | O |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ 44 | 1 1| 20 |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ 64 | 1 1| 26 |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ 92 | 0 | |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+The domain name for F.ISI.ARPA is shown at offset 20. The domain name
+FOO.F.ISI.ARPA is shown at offset 40; this definition uses a pointer to
+concatenate a label for FOO to the previously defined F.ISI.ARPA. The
+domain name ARPA is defined at offset 64 using a pointer to the ARPA
+component of the name F.ISI.ARPA at 20; note that this pointer relies on
+ARPA being the last label in the string at 20. The root domain name is
+
+
+
+Mockapetris [Page 31]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+defined by a single octet of zeros at 92; the root domain name has no
+labels.
+
+4.2. Transport
+
+The DNS assumes that messages will be transmitted as datagrams or in a
+byte stream carried by a virtual circuit. While virtual circuits can be
+used for any DNS activity, datagrams are preferred for queries due to
+their lower overhead and better performance. Zone refresh activities
+must use virtual circuits because of the need for reliable transfer.
+
+The Internet supports name server access using TCP [RFC-793] on server
+port 53 (decimal) as well as datagram access using UDP [RFC-768] on UDP
+port 53 (decimal).
+
+4.2.1. UDP usage
+
+Messages sent using UDP user server port 53 (decimal).
+
+Messages carried by UDP are restricted to 512 bytes (not counting the IP
+or UDP headers). Longer messages are truncated and the TC bit is set in
+the header.
+
+UDP is not acceptable for zone transfers, but is the recommended method
+for standard queries in the Internet. Queries sent using UDP may be
+lost, and hence a retransmission strategy is required. Queries or their
+responses may be reordered by the network, or by processing in name
+servers, so resolvers should not depend on them being returned in order.
+
+The optimal UDP retransmission policy will vary with performance of the
+Internet and the needs of the client, but the following are recommended:
+
+ - The client should try other servers and server addresses
+ before repeating a query to a specific address of a server.
+
+ - The retransmission interval should be based on prior
+ statistics if possible. Too aggressive retransmission can
+ easily slow responses for the community at large. Depending
+ on how well connected the client is to its expected servers,
+ the minimum retransmission interval should be 2-5 seconds.
+
+More suggestions on server selection and retransmission policy can be
+found in the resolver section of this memo.
+
+4.2.2. TCP usage
+
+Messages sent over TCP connections use server port 53 (decimal). The
+message is prefixed with a two byte length field which gives the message
+
+
+
+Mockapetris [Page 32]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+length, excluding the two byte length field. This length field allows
+the low-level processing to assemble a complete message before beginning
+to parse it.
+
+Several connection management policies are recommended:
+
+ - The server should not block other activities waiting for TCP
+ data.
+
+ - The server should support multiple connections.
+
+ - The server should assume that the client will initiate
+ connection closing, and should delay closing its end of the
+ connection until all outstanding client requests have been
+ satisfied.
+
+ - If the server needs to close a dormant connection to reclaim
+ resources, it should wait until the connection has been idle
+ for a period on the order of two minutes. In particular, the
+ server should allow the SOA and AXFR request sequence (which
+ begins a refresh operation) to be made on a single connection.
+ Since the server would be unable to answer queries anyway, a
+ unilateral close or reset may be used instead of a graceful
+ close.
+
+5. MASTER FILES
+
+Master files are text files that contain RRs in text form. Since the
+contents of a zone can be expressed in the form of a list of RRs a
+master file is most often used to define a zone, though it can be used
+to list a cache's contents. Hence, this section first discusses the
+format of RRs in a master file, and then the special considerations when
+a master file is used to create a zone in some name server.
+
+5.1. Format
+
+The format of these files is a sequence of entries. Entries are
+predominantly line-oriented, though parentheses can be used to continue
+a list of items across a line boundary, and text literals can contain
+CRLF within the text. Any combination of tabs and spaces act as a
+delimiter between the separate items that make up an entry. The end of
+any line in the master file can end with a comment. The comment starts
+with a ";" (semicolon).
+
+The following entries are defined:
+
+ <blank>[<comment>]
+
+
+
+
+Mockapetris [Page 33]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+ $ORIGIN <domain-name> [<comment>]
+
+ $INCLUDE <file-name> [<domain-name>] [<comment>]
+
+ <domain-name><rr> [<comment>]
+
+ <blank><rr> [<comment>]
+
+Blank lines, with or without comments, are allowed anywhere in the file.
+
+Two control entries are defined: $ORIGIN and $INCLUDE. $ORIGIN is
+followed by a domain name, and resets the current origin for relative
+domain names to the stated name. $INCLUDE inserts the named file into
+the current file, and may optionally specify a domain name that sets the
+relative domain name origin for the included file. $INCLUDE may also
+have a comment. Note that a $INCLUDE entry never changes the relative
+origin of the parent file, regardless of changes to the relative origin
+made within the included file.
+
+The last two forms represent RRs. If an entry for an RR begins with a
+blank, then the RR is assumed to be owned by the last stated owner. If
+an RR entry begins with a <domain-name>, then the owner name is reset.
+
+<rr> contents take one of the following forms:
+
+ [<TTL>] [<class>] <type> <RDATA>
+
+ [<class>] [<TTL>] <type> <RDATA>
+
+The RR begins with optional TTL and class fields, followed by a type and
+RDATA field appropriate to the type and class. Class and type use the
+standard mnemonics, TTL is a decimal integer. Omitted class and TTL
+values are default to the last explicitly stated values. Since type and
+class mnemonics are disjoint, the parse is unique. (Note that this
+order is different from the order used in examples and the order used in
+the actual RRs; the given order allows easier parsing and defaulting.)
+
+<domain-name>s make up a large share of the data in the master file.
+The labels in the domain name are expressed as character strings and
+separated by dots. Quoting conventions allow arbitrary characters to be
+stored in domain names. Domain names that end in a dot are called
+absolute, and are taken as complete. Domain names which do not end in a
+dot are called relative; the actual domain name is the concatenation of
+the relative part with an origin specified in a $ORIGIN, $INCLUDE, or as
+an argument to the master file loading routine. A relative name is an
+error when no origin is available.
+
+
+
+
+
+Mockapetris [Page 34]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+<character-string> is expressed in one or two ways: as a contiguous set
+of characters without interior spaces, or as a string beginning with a "
+and ending with a ". Inside a " delimited string any character can
+occur, except for a " itself, which must be quoted using \ (back slash).
+
+Because these files are text files several special encodings are
+necessary to allow arbitrary data to be loaded. In particular:
+
+ of the root.
+
+@ A free standing @ is used to denote the current origin.
+
+\X where X is any character other than a digit (0-9), is
+ used to quote that character so that its special meaning
+ does not apply. For example, "\." can be used to place
+ a dot character in a label.
+
+\DDD where each D is a digit is the octet corresponding to
+ the decimal number described by DDD. The resulting
+ octet is assumed to be text and is not checked for
+ special meaning.
+
+( ) Parentheses are used to group data that crosses a line
+ boundary. In effect, line terminations are not
+ recognized within parentheses.
+
+; Semicolon is used to start a comment; the remainder of
+ the line is ignored.
+
+5.2. Use of master files to define zones
+
+When a master file is used to load a zone, the operation should be
+suppressed if any errors are encountered in the master file. The
+rationale for this is that a single error can have widespread
+consequences. For example, suppose that the RRs defining a delegation
+have syntax errors; then the server will return authoritative name
+errors for all names in the subzone (except in the case where the
+subzone is also present on the server).
+
+Several other validity checks that should be performed in addition to
+insuring that the file is syntactically correct:
+
+ 1. All RRs in the file should have the same class.
+
+ 2. Exactly one SOA RR should be present at the top of the zone.
+
+ 3. If delegations are present and glue information is required,
+ it should be present.
+
+
+
+Mockapetris [Page 35]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+ 4. Information present outside of the authoritative nodes in the
+ zone should be glue information, rather than the result of an
+ origin or similar error.
+
+5.3. Master file example
+
+The following is an example file which might be used to define the
+ISI.EDU zone.and is loaded with an origin of ISI.EDU:
+
+@ IN SOA VENERA Action\.domains (
+ 20 ; SERIAL
+ 7200 ; REFRESH
+ 600 ; RETRY
+ 3600000; EXPIRE
+ 60) ; MINIMUM
+
+ NS A.ISI.EDU.
+ NS VENERA
+ NS VAXA
+ MX 10 VENERA
+ MX 20 VAXA
+
+A A 26.3.0.103
+
+VENERA A 10.1.0.52
+ A 128.9.0.32
+
+VAXA A 10.2.0.27
+ A 128.9.0.33
+
+
+$INCLUDE <SUBSYS>ISI-MAILBOXES.TXT
+
+Where the file <SUBSYS>ISI-MAILBOXES.TXT is:
+
+ MOE MB A.ISI.EDU.
+ LARRY MB A.ISI.EDU.
+ CURLEY MB A.ISI.EDU.
+ STOOGES MG MOE
+ MG LARRY
+ MG CURLEY
+
+Note the use of the \ character in the SOA RR to specify the responsible
+person mailbox "Action.domains@E.ISI.EDU".
+
+
+
+
+
+
+
+Mockapetris [Page 36]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+6. NAME SERVER IMPLEMENTATION
+
+6.1. Architecture
+
+The optimal structure for the name server will depend on the host
+operating system and whether the name server is integrated with resolver
+operations, either by supporting recursive service, or by sharing its
+database with a resolver. This section discusses implementation
+considerations for a name server which shares a database with a
+resolver, but most of these concerns are present in any name server.
+
+6.1.1. Control
+
+A name server must employ multiple concurrent activities, whether they
+are implemented as separate tasks in the host's OS or multiplexing
+inside a single name server program. It is simply not acceptable for a
+name server to block the service of UDP requests while it waits for TCP
+data for refreshing or query activities. Similarly, a name server
+should not attempt to provide recursive service without processing such
+requests in parallel, though it may choose to serialize requests from a
+single client, or to regard identical requests from the same client as
+duplicates. A name server should not substantially delay requests while
+it reloads a zone from master files or while it incorporates a newly
+refreshed zone into its database.
+
+6.1.2. Database
+
+While name server implementations are free to use any internal data
+structures they choose, the suggested structure consists of three major
+parts:
+
+ - A "catalog" data structure which lists the zones available to
+ this server, and a "pointer" to the zone data structure. The
+ main purpose of this structure is to find the nearest ancestor
+ zone, if any, for arriving standard queries.
+
+ - Separate data structures for each of the zones held by the
+ name server.
+
+ - A data structure for cached data. (or perhaps separate caches
+ for different classes)
+
+All of these data structures can be implemented an identical tree
+structure format, with different data chained off the nodes in different
+parts: in the catalog the data is pointers to zones, while in the zone
+and cache data structures, the data will be RRs. In designing the tree
+framework the designer should recognize that query processing will need
+to traverse the tree using case-insensitive label comparisons; and that
+
+
+
+Mockapetris [Page 37]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+in real data, a few nodes have a very high branching factor (100-1000 or
+more), but the vast majority have a very low branching factor (0-1).
+
+One way to solve the case problem is to store the labels for each node
+in two pieces: a standardized-case representation of the label where all
+ASCII characters are in a single case, together with a bit mask that
+denotes which characters are actually of a different case. The
+branching factor diversity can be handled using a simple linked list for
+a node until the branching factor exceeds some threshold, and
+transitioning to a hash structure after the threshold is exceeded. In
+any case, hash structures used to store tree sections must insure that
+hash functions and procedures preserve the casing conventions of the
+DNS.
+
+The use of separate structures for the different parts of the database
+is motivated by several factors:
+
+ - The catalog structure can be an almost static structure that
+ need change only when the system administrator changes the
+ zones supported by the server. This structure can also be
+ used to store parameters used to control refreshing
+ activities.
+
+ - The individual data structures for zones allow a zone to be
+ replaced simply by changing a pointer in the catalog. Zone
+ refresh operations can build a new structure and, when
+ complete, splice it into the database via a simple pointer
+ replacement. It is very important that when a zone is
+ refreshed, queries should not use old and new data
+ simultaneously.
+
+ - With the proper search procedures, authoritative data in zones
+ will always "hide", and hence take precedence over, cached
+ data.
+
+ - Errors in zone definitions that cause overlapping zones, etc.,
+ may cause erroneous responses to queries, but problem
+ determination is simplified, and the contents of one "bad"
+ zone can't corrupt another.
+
+ - Since the cache is most frequently updated, it is most
+ vulnerable to corruption during system restarts. It can also
+ become full of expired RR data. In either case, it can easily
+ be discarded without disturbing zone data.
+
+A major aspect of database design is selecting a structure which allows
+the name server to deal with crashes of the name server's host. State
+information which a name server should save across system crashes
+
+
+
+Mockapetris [Page 38]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+includes the catalog structure (including the state of refreshing for
+each zone) and the zone data itself.
+
+6.1.3. Time
+
+Both the TTL data for RRs and the timing data for refreshing activities
+depends on 32 bit timers in units of seconds. Inside the database,
+refresh timers and TTLs for cached data conceptually "count down", while
+data in the zone stays with constant TTLs.
+
+A recommended implementation strategy is to store time in two ways: as
+a relative increment and as an absolute time. One way to do this is to
+use positive 32 bit numbers for one type and negative numbers for the
+other. The RRs in zones use relative times; the refresh timers and
+cache data use absolute times. Absolute numbers are taken with respect
+to some known origin and converted to relative values when placed in the
+response to a query. When an absolute TTL is negative after conversion
+to relative, then the data is expired and should be ignored.
+
+6.2. Standard query processing
+
+The major algorithm for standard query processing is presented in
+[RFC-1034].
+
+When processing queries with QCLASS=*, or some other QCLASS which
+matches multiple classes, the response should never be authoritative
+unless the server can guarantee that the response covers all classes.
+
+When composing a response, RRs which are to be inserted in the
+additional section, but duplicate RRs in the answer or authority
+sections, may be omitted from the additional section.
+
+When a response is so long that truncation is required, the truncation
+should start at the end of the response and work forward in the
+datagram. Thus if there is any data for the authority section, the
+answer section is guaranteed to be unique.
+
+The MINIMUM value in the SOA should be used to set a floor on the TTL of
+data distributed from a zone. This floor function should be done when
+the data is copied into a response. This will allow future dynamic
+update protocols to change the SOA MINIMUM field without ambiguous
+semantics.
+
+6.3. Zone refresh and reload processing
+
+In spite of a server's best efforts, it may be unable to load zone data
+from a master file due to syntax errors, etc., or be unable to refresh a
+zone within the its expiration parameter. In this case, the name server
+
+
+
+Mockapetris [Page 39]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+should answer queries as if it were not supposed to possess the zone.
+
+If a master is sending a zone out via AXFR, and a new version is created
+during the transfer, the master should continue to send the old version
+if possible. In any case, it should never send part of one version and
+part of another. If completion is not possible, the master should reset
+the connection on which the zone transfer is taking place.
+
+6.4. Inverse queries (Optional)
+
+Inverse queries are an optional part of the DNS. Name servers are not
+required to support any form of inverse queries. If a name server
+receives an inverse query that it does not support, it returns an error
+response with the "Not Implemented" error set in the header. While
+inverse query support is optional, all name servers must be at least
+able to return the error response.
+
+6.4.1. The contents of inverse queries and responses Inverse
+queries reverse the mappings performed by standard query operations;
+while a standard query maps a domain name to a resource, an inverse
+query maps a resource to a domain name. For example, a standard query
+might bind a domain name to a host address; the corresponding inverse
+query binds the host address to a domain name.
+
+Inverse queries take the form of a single RR in the answer section of
+the message, with an empty question section. The owner name of the
+query RR and its TTL are not significant. The response carries
+questions in the question section which identify all names possessing
+the query RR WHICH THE NAME SERVER KNOWS. Since no name server knows
+about all of the domain name space, the response can never be assumed to
+be complete. Thus inverse queries are primarily useful for database
+management and debugging activities. Inverse queries are NOT an
+acceptable method of mapping host addresses to host names; use the IN-
+ADDR.ARPA domain instead.
+
+Where possible, name servers should provide case-insensitive comparisons
+for inverse queries. Thus an inverse query asking for an MX RR of
+"Venera.isi.edu" should get the same response as a query for
+"VENERA.ISI.EDU"; an inverse query for HINFO RR "IBM-PC UNIX" should
+produce the same result as an inverse query for "IBM-pc unix". However,
+this cannot be guaranteed because name servers may possess RRs that
+contain character strings but the name server does not know that the
+data is character.
+
+When a name server processes an inverse query, it either returns:
+
+ 1. zero, one, or multiple domain names for the specified
+ resource as QNAMEs in the question section
+
+
+
+Mockapetris [Page 40]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+ 2. an error code indicating that the name server doesn't support
+ inverse mapping of the specified resource type.
+
+When the response to an inverse query contains one or more QNAMEs, the
+owner name and TTL of the RR in the answer section which defines the
+inverse query is modified to exactly match an RR found at the first
+QNAME.
+
+RRs returned in the inverse queries cannot be cached using the same
+mechanism as is used for the replies to standard queries. One reason
+for this is that a name might have multiple RRs of the same type, and
+only one would appear. For example, an inverse query for a single
+address of a multiply homed host might create the impression that only
+one address existed.
+
+6.4.2. Inverse query and response example The overall structure
+of an inverse query for retrieving the domain name that corresponds to
+Internet address 10.1.0.52 is shown below:
+
+ +-----------------------------------------+
+ Header | OPCODE=IQUERY, ID=997 |
+ +-----------------------------------------+
+ Question | <empty> |
+ +-----------------------------------------+
+ Answer | <anyname> A IN 10.1.0.52 |
+ +-----------------------------------------+
+ Authority | <empty> |
+ +-----------------------------------------+
+ Additional | <empty> |
+ +-----------------------------------------+
+
+This query asks for a question whose answer is the Internet style
+address 10.1.0.52. Since the owner name is not known, any domain name
+can be used as a placeholder (and is ignored). A single octet of zero,
+signifying the root, is usually used because it minimizes the length of
+the message. The TTL of the RR is not significant. The response to
+this query might be:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Mockapetris [Page 41]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+ +-----------------------------------------+
+ Header | OPCODE=RESPONSE, ID=997 |
+ +-----------------------------------------+
+ Question |QTYPE=A, QCLASS=IN, QNAME=VENERA.ISI.EDU |
+ +-----------------------------------------+
+ Answer | VENERA.ISI.EDU A IN 10.1.0.52 |
+ +-----------------------------------------+
+ Authority | <empty> |
+ +-----------------------------------------+
+ Additional | <empty> |
+ +-----------------------------------------+
+
+Note that the QTYPE in a response to an inverse query is the same as the
+TYPE field in the answer section of the inverse query. Responses to
+inverse queries may contain multiple questions when the inverse is not
+unique. If the question section in the response is not empty, then the
+RR in the answer section is modified to correspond to be an exact copy
+of an RR at the first QNAME.
+
+6.4.3. Inverse query processing
+
+Name servers that support inverse queries can support these operations
+through exhaustive searches of their databases, but this becomes
+impractical as the size of the database increases. An alternative
+approach is to invert the database according to the search key.
+
+For name servers that support multiple zones and a large amount of data,
+the recommended approach is separate inversions for each zone. When a
+particular zone is changed during a refresh, only its inversions need to
+be redone.
+
+Support for transfer of this type of inversion may be included in future
+versions of the domain system, but is not supported in this version.
+
+6.5. Completion queries and responses
+
+The optional completion services described in RFC-882 and RFC-883 have
+been deleted. Redesigned services may become available in the future.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Mockapetris [Page 42]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+7. RESOLVER IMPLEMENTATION
+
+The top levels of the recommended resolver algorithm are discussed in
+[RFC-1034]. This section discusses implementation details assuming the
+database structure suggested in the name server implementation section
+of this memo.
+
+7.1. Transforming a user request into a query
+
+The first step a resolver takes is to transform the client's request,
+stated in a format suitable to the local OS, into a search specification
+for RRs at a specific name which match a specific QTYPE and QCLASS.
+Where possible, the QTYPE and QCLASS should correspond to a single type
+and a single class, because this makes the use of cached data much
+simpler. The reason for this is that the presence of data of one type
+in a cache doesn't confirm the existence or non-existence of data of
+other types, hence the only way to be sure is to consult an
+authoritative source. If QCLASS=* is used, then authoritative answers
+won't be available.
+
+Since a resolver must be able to multiplex multiple requests if it is to
+perform its function efficiently, each pending request is usually
+represented in some block of state information. This state block will
+typically contain:
+
+ - A timestamp indicating the time the request began.
+ The timestamp is used to decide whether RRs in the database
+ can be used or are out of date. This timestamp uses the
+ absolute time format previously discussed for RR storage in
+ zones and caches. Note that when an RRs TTL indicates a
+ relative time, the RR must be timely, since it is part of a
+ zone. When the RR has an absolute time, it is part of a
+ cache, and the TTL of the RR is compared against the timestamp
+ for the start of the request.
+
+ Note that using the timestamp is superior to using a current
+ time, since it allows RRs with TTLs of zero to be entered in
+ the cache in the usual manner, but still used by the current
+ request, even after intervals of many seconds due to system
+ load, query retransmission timeouts, etc.
+
+ - Some sort of parameters to limit the amount of work which will
+ be performed for this request.
+
+ The amount of work which a resolver will do in response to a
+ client request must be limited to guard against errors in the
+ database, such as circular CNAME references, and operational
+ problems, such as network partition which prevents the
+
+
+
+Mockapetris [Page 43]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+ resolver from accessing the name servers it needs. While
+ local limits on the number of times a resolver will retransmit
+ a particular query to a particular name server address are
+ essential, the resolver should have a global per-request
+ counter to limit work on a single request. The counter should
+ be set to some initial value and decremented whenever the
+ resolver performs any action (retransmission timeout,
+ retransmission, etc.) If the counter passes zero, the request
+ is terminated with a temporary error.
+
+ Note that if the resolver structure allows one request to
+ start others in parallel, such as when the need to access a
+ name server for one request causes a parallel resolve for the
+ name server's addresses, the spawned request should be started
+ with a lower counter. This prevents circular references in
+ the database from starting a chain reaction of resolver
+ activity.
+
+ - The SLIST data structure discussed in [RFC-1034].
+
+ This structure keeps track of the state of a request if it
+ must wait for answers from foreign name servers.
+
+7.2. Sending the queries
+
+As described in [RFC-1034], the basic task of the resolver is to
+formulate a query which will answer the client's request and direct that
+query to name servers which can provide the information. The resolver
+will usually only have very strong hints about which servers to ask, in
+the form of NS RRs, and may have to revise the query, in response to
+CNAMEs, or revise the set of name servers the resolver is asking, in
+response to delegation responses which point the resolver to name
+servers closer to the desired information. In addition to the
+information requested by the client, the resolver may have to call upon
+its own services to determine the address of name servers it wishes to
+contact.
+
+In any case, the model used in this memo assumes that the resolver is
+multiplexing attention between multiple requests, some from the client,
+and some internally generated. Each request is represented by some
+state information, and the desired behavior is that the resolver
+transmit queries to name servers in a way that maximizes the probability
+that the request is answered, minimizes the time that the request takes,
+and avoids excessive transmissions. The key algorithm uses the state
+information of the request to select the next name server address to
+query, and also computes a timeout which will cause the next action
+should a response not arrive. The next action will usually be a
+transmission to some other server, but may be a temporary error to the
+
+
+
+Mockapetris [Page 44]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+client.
+
+The resolver always starts with a list of server names to query (SLIST).
+This list will be all NS RRs which correspond to the nearest ancestor
+zone that the resolver knows about. To avoid startup problems, the
+resolver should have a set of default servers which it will ask should
+it have no current NS RRs which are appropriate. The resolver then adds
+to SLIST all of the known addresses for the name servers, and may start
+parallel requests to acquire the addresses of the servers when the
+resolver has the name, but no addresses, for the name servers.
+
+To complete initialization of SLIST, the resolver attaches whatever
+history information it has to the each address in SLIST. This will
+usually consist of some sort of weighted averages for the response time
+of the address, and the batting average of the address (i.e., how often
+the address responded at all to the request). Note that this
+information should be kept on a per address basis, rather than on a per
+name server basis, because the response time and batting average of a
+particular server may vary considerably from address to address. Note
+also that this information is actually specific to a resolver address /
+server address pair, so a resolver with multiple addresses may wish to
+keep separate histories for each of its addresses. Part of this step
+must deal with addresses which have no such history; in this case an
+expected round trip time of 5-10 seconds should be the worst case, with
+lower estimates for the same local network, etc.
+
+Note that whenever a delegation is followed, the resolver algorithm
+reinitializes SLIST.
+
+The information establishes a partial ranking of the available name
+server addresses. Each time an address is chosen and the state should
+be altered to prevent its selection again until all other addresses have
+been tried. The timeout for each transmission should be 50-100% greater
+than the average predicted value to allow for variance in response.
+
+Some fine points:
+
+ - The resolver may encounter a situation where no addresses are
+ available for any of the name servers named in SLIST, and
+ where the servers in the list are precisely those which would
+ normally be used to look up their own addresses. This
+ situation typically occurs when the glue address RRs have a
+ smaller TTL than the NS RRs marking delegation, or when the
+ resolver caches the result of a NS search. The resolver
+ should detect this condition and restart the search at the
+ next ancestor zone, or alternatively at the root.
+
+
+
+
+
+Mockapetris [Page 45]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+ - If a resolver gets a server error or other bizarre response
+ from a name server, it should remove it from SLIST, and may
+ wish to schedule an immediate transmission to the next
+ candidate server address.
+
+7.3. Processing responses
+
+The first step in processing arriving response datagrams is to parse the
+response. This procedure should include:
+
+ - Check the header for reasonableness. Discard datagrams which
+ are queries when responses are expected.
+
+ - Parse the sections of the message, and insure that all RRs are
+ correctly formatted.
+
+ - As an optional step, check the TTLs of arriving data looking
+ for RRs with excessively long TTLs. If a RR has an
+ excessively long TTL, say greater than 1 week, either discard
+ the whole response, or limit all TTLs in the response to 1
+ week.
+
+The next step is to match the response to a current resolver request.
+The recommended strategy is to do a preliminary matching using the ID
+field in the domain header, and then to verify that the question section
+corresponds to the information currently desired. This requires that
+the transmission algorithm devote several bits of the domain ID field to
+a request identifier of some sort. This step has several fine points:
+
+ - Some name servers send their responses from different
+ addresses than the one used to receive the query. That is, a
+ resolver cannot rely that a response will come from the same
+ address which it sent the corresponding query to. This name
+ server bug is typically encountered in UNIX systems.
+
+ - If the resolver retransmits a particular request to a name
+ server it should be able to use a response from any of the
+ transmissions. However, if it is using the response to sample
+ the round trip time to access the name server, it must be able
+ to determine which transmission matches the response (and keep
+ transmission times for each outgoing message), or only
+ calculate round trip times based on initial transmissions.
+
+ - A name server will occasionally not have a current copy of a
+ zone which it should have according to some NS RRs. The
+ resolver should simply remove the name server from the current
+ SLIST, and continue.
+
+
+
+
+Mockapetris [Page 46]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+7.4. Using the cache
+
+In general, we expect a resolver to cache all data which it receives in
+responses since it may be useful in answering future client requests.
+However, there are several types of data which should not be cached:
+
+ - When several RRs of the same type are available for a
+ particular owner name, the resolver should either cache them
+ all or none at all. When a response is truncated, and a
+ resolver doesn't know whether it has a complete set, it should
+ not cache a possibly partial set of RRs.
+
+ - Cached data should never be used in preference to
+ authoritative data, so if caching would cause this to happen
+ the data should not be cached.
+
+ - The results of an inverse query should not be cached.
+
+ - The results of standard queries where the QNAME contains "*"
+ labels if the data might be used to construct wildcards. The
+ reason is that the cache does not necessarily contain existing
+ RRs or zone boundary information which is necessary to
+ restrict the application of the wildcard RRs.
+
+ - RR data in responses of dubious reliability. When a resolver
+ receives unsolicited responses or RR data other than that
+ requested, it should discard it without caching it. The basic
+ implication is that all sanity checks on a packet should be
+ performed before any of it is cached.
+
+In a similar vein, when a resolver has a set of RRs for some name in a
+response, and wants to cache the RRs, it should check its cache for
+already existing RRs. Depending on the circumstances, either the data
+in the response or the cache is preferred, but the two should never be
+combined. If the data in the response is from authoritative data in the
+answer section, it is always preferred.
+
+8. MAIL SUPPORT
+
+The domain system defines a standard for mapping mailboxes into domain
+names, and two methods for using the mailbox information to derive mail
+routing information. The first method is called mail exchange binding
+and the other method is mailbox binding. The mailbox encoding standard
+and mail exchange binding are part of the DNS official protocol, and are
+the recommended method for mail routing in the Internet. Mailbox
+binding is an experimental feature which is still under development and
+subject to change.
+
+
+
+
+Mockapetris [Page 47]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+The mailbox encoding standard assumes a mailbox name of the form
+"<local-part>@<mail-domain>". While the syntax allowed in each of these
+sections varies substantially between the various mail internets, the
+preferred syntax for the ARPA Internet is given in [RFC-822].
+
+The DNS encodes the <local-part> as a single label, and encodes the
+<mail-domain> as a domain name. The single label from the <local-part>
+is prefaced to the domain name from <mail-domain> to form the domain
+name corresponding to the mailbox. Thus the mailbox HOSTMASTER@SRI-
+NIC.ARPA is mapped into the domain name HOSTMASTER.SRI-NIC.ARPA. If the
+<local-part> contains dots or other special characters, its
+representation in a master file will require the use of backslash
+quoting to ensure that the domain name is properly encoded. For
+example, the mailbox Action.domains@ISI.EDU would be represented as
+Action\.domains.ISI.EDU.
+
+8.1. Mail exchange binding
+
+Mail exchange binding uses the <mail-domain> part of a mailbox
+specification to determine where mail should be sent. The <local-part>
+is not even consulted. [RFC-974] specifies this method in detail, and
+should be consulted before attempting to use mail exchange support.
+
+One of the advantages of this method is that it decouples mail
+destination naming from the hosts used to support mail service, at the
+cost of another layer of indirection in the lookup function. However,
+the addition layer should eliminate the need for complicated "%", "!",
+etc encodings in <local-part>.
+
+The essence of the method is that the <mail-domain> is used as a domain
+name to locate type MX RRs which list hosts willing to accept mail for
+<mail-domain>, together with preference values which rank the hosts
+according to an order specified by the administrators for <mail-domain>.
+
+In this memo, the <mail-domain> ISI.EDU is used in examples, together
+with the hosts VENERA.ISI.EDU and VAXA.ISI.EDU as mail exchanges for
+ISI.EDU. If a mailer had a message for Mockapetris@ISI.EDU, it would
+route it by looking up MX RRs for ISI.EDU. The MX RRs at ISI.EDU name
+VENERA.ISI.EDU and VAXA.ISI.EDU, and type A queries can find the host
+addresses.
+
+8.2. Mailbox binding (Experimental)
+
+In mailbox binding, the mailer uses the entire mail destination
+specification to construct a domain name. The encoded domain name for
+the mailbox is used as the QNAME field in a QTYPE=MAILB query.
+
+Several outcomes are possible for this query:
+
+
+
+Mockapetris [Page 48]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+ 1. The query can return a name error indicating that the mailbox
+ does not exist as a domain name.
+
+ In the long term, this would indicate that the specified
+ mailbox doesn't exist. However, until the use of mailbox
+ binding is universal, this error condition should be
+ interpreted to mean that the organization identified by the
+ global part does not support mailbox binding. The
+ appropriate procedure is to revert to exchange binding at
+ this point.
+
+ 2. The query can return a Mail Rename (MR) RR.
+
+ The MR RR carries new mailbox specification in its RDATA
+ field. The mailer should replace the old mailbox with the
+ new one and retry the operation.
+
+ 3. The query can return a MB RR.
+
+ The MB RR carries a domain name for a host in its RDATA
+ field. The mailer should deliver the message to that host
+ via whatever protocol is applicable, e.g., b,SMTP.
+
+ 4. The query can return one or more Mail Group (MG) RRs.
+
+ This condition means that the mailbox was actually a mailing
+ list or mail group, rather than a single mailbox. Each MG RR
+ has a RDATA field that identifies a mailbox that is a member
+ of the group. The mailer should deliver a copy of the
+ message to each member.
+
+ 5. The query can return a MB RR as well as one or more MG RRs.
+
+ This condition means the the mailbox was actually a mailing
+ list. The mailer can either deliver the message to the host
+ specified by the MB RR, which will in turn do the delivery to
+ all members, or the mailer can use the MG RRs to do the
+ expansion itself.
+
+In any of these cases, the response may include a Mail Information
+(MINFO) RR. This RR is usually associated with a mail group, but is
+legal with a MB. The MINFO RR identifies two mailboxes. One of these
+identifies a responsible person for the original mailbox name. This
+mailbox should be used for requests to be added to a mail group, etc.
+The second mailbox name in the MINFO RR identifies a mailbox that should
+receive error messages for mail failures. This is particularly
+appropriate for mailing lists when errors in member names should be
+reported to a person other than the one who sends a message to the list.
+
+
+
+Mockapetris [Page 49]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+New fields may be added to this RR in the future.
+
+
+9. REFERENCES and BIBLIOGRAPHY
+
+[Dyer 87] S. Dyer, F. Hsu, "Hesiod", Project Athena
+ Technical Plan - Name Service, April 1987, version 1.9.
+
+ Describes the fundamentals of the Hesiod name service.
+
+[IEN-116] J. Postel, "Internet Name Server", IEN-116,
+ USC/Information Sciences Institute, August 1979.
+
+ A name service obsoleted by the Domain Name System, but
+ still in use.
+
+[Quarterman 86] J. Quarterman, and J. Hoskins, "Notable Computer Networks",
+ Communications of the ACM, October 1986, volume 29, number
+ 10.
+
+[RFC-742] K. Harrenstien, "NAME/FINGER", RFC-742, Network
+ Information Center, SRI International, December 1977.
+
+[RFC-768] J. Postel, "User Datagram Protocol", RFC-768,
+ USC/Information Sciences Institute, August 1980.
+
+[RFC-793] J. Postel, "Transmission Control Protocol", RFC-793,
+ USC/Information Sciences Institute, September 1981.
+
+[RFC-799] D. Mills, "Internet Name Domains", RFC-799, COMSAT,
+ September 1981.
+
+ Suggests introduction of a hierarchy in place of a flat
+ name space for the Internet.
+
+[RFC-805] J. Postel, "Computer Mail Meeting Notes", RFC-805,
+ USC/Information Sciences Institute, February 1982.
+
+[RFC-810] E. Feinler, K. Harrenstien, Z. Su, and V. White, "DOD
+ Internet Host Table Specification", RFC-810, Network
+ Information Center, SRI International, March 1982.
+
+ Obsolete. See RFC-952.
+
+[RFC-811] K. Harrenstien, V. White, and E. Feinler, "Hostnames
+ Server", RFC-811, Network Information Center, SRI
+ International, March 1982.
+
+
+
+
+Mockapetris [Page 50]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+ Obsolete. See RFC-953.
+
+[RFC-812] K. Harrenstien, and V. White, "NICNAME/WHOIS", RFC-812,
+ Network Information Center, SRI International, March
+ 1982.
+
+[RFC-819] Z. Su, and J. Postel, "The Domain Naming Convention for
+ Internet User Applications", RFC-819, Network
+ Information Center, SRI International, August 1982.
+
+ Early thoughts on the design of the domain system.
+ Current implementation is completely different.
+
+[RFC-821] J. Postel, "Simple Mail Transfer Protocol", RFC-821,
+ USC/Information Sciences Institute, August 1980.
+
+[RFC-830] Z. Su, "A Distributed System for Internet Name Service",
+ RFC-830, Network Information Center, SRI International,
+ October 1982.
+
+ Early thoughts on the design of the domain system.
+ Current implementation is completely different.
+
+[RFC-882] P. Mockapetris, "Domain names - Concepts and
+ Facilities," RFC-882, USC/Information Sciences
+ Institute, November 1983.
+
+ Superceeded by this memo.
+
+[RFC-883] P. Mockapetris, "Domain names - Implementation and
+ Specification," RFC-883, USC/Information Sciences
+ Institute, November 1983.
+
+ Superceeded by this memo.
+
+[RFC-920] J. Postel and J. Reynolds, "Domain Requirements",
+ RFC-920, USC/Information Sciences Institute,
+ October 1984.
+
+ Explains the naming scheme for top level domains.
+
+[RFC-952] K. Harrenstien, M. Stahl, E. Feinler, "DoD Internet Host
+ Table Specification", RFC-952, SRI, October 1985.
+
+ Specifies the format of HOSTS.TXT, the host/address
+ table replaced by the DNS.
+
+
+
+
+
+Mockapetris [Page 51]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+[RFC-953] K. Harrenstien, M. Stahl, E. Feinler, "HOSTNAME Server",
+ RFC-953, SRI, October 1985.
+
+ This RFC contains the official specification of the
+ hostname server protocol, which is obsoleted by the DNS.
+ This TCP based protocol accesses information stored in
+ the RFC-952 format, and is used to obtain copies of the
+ host table.
+
+[RFC-973] P. Mockapetris, "Domain System Changes and
+ Observations", RFC-973, USC/Information Sciences
+ Institute, January 1986.
+
+ Describes changes to RFC-882 and RFC-883 and reasons for
+ them.
+
+[RFC-974] C. Partridge, "Mail routing and the domain system",
+ RFC-974, CSNET CIC BBN Labs, January 1986.
+
+ Describes the transition from HOSTS.TXT based mail
+ addressing to the more powerful MX system used with the
+ domain system.
+
+[RFC-1001] NetBIOS Working Group, "Protocol standard for a NetBIOS
+ service on a TCP/UDP transport: Concepts and Methods",
+ RFC-1001, March 1987.
+
+ This RFC and RFC-1002 are a preliminary design for
+ NETBIOS on top of TCP/IP which proposes to base NetBIOS
+ name service on top of the DNS.
+
+[RFC-1002] NetBIOS Working Group, "Protocol standard for a NetBIOS
+ service on a TCP/UDP transport: Detailed
+ Specifications", RFC-1002, March 1987.
+
+[RFC-1010] J. Reynolds, and J. Postel, "Assigned Numbers", RFC-1010,
+ USC/Information Sciences Institute, May 1987.
+
+ Contains socket numbers and mnemonics for host names,
+ operating systems, etc.
+
+[RFC-1031] W. Lazear, "MILNET Name Domain Transition", RFC-1031,
+ November 1987.
+
+ Describes a plan for converting the MILNET to the DNS.
+
+[RFC-1032] M. Stahl, "Establishing a Domain - Guidelines for
+ Administrators", RFC-1032, November 1987.
+
+
+
+Mockapetris [Page 52]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+ Describes the registration policies used by the NIC to
+ administer the top level domains and delegate subzones.
+
+[RFC-1033] M. Lottor, "Domain Administrators Operations Guide",
+ RFC-1033, November 1987.
+
+ A cookbook for domain administrators.
+
+[Solomon 82] M. Solomon, L. Landweber, and D. Neuhengen, "The CSNET
+ Name Server", Computer Networks, vol 6, nr 3, July 1982.
+
+ Describes a name service for CSNET which is independent
+ from the DNS and DNS use in the CSNET.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Mockapetris [Page 53]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+Index
+
+ * 13
+
+ ; 33, 35
+
+ <character-string> 35
+ <domain-name> 34
+
+ @ 35
+
+ \ 35
+
+ A 12
+
+ Byte order 8
+
+ CH 13
+ Character case 9
+ CLASS 11
+ CNAME 12
+ Completion 42
+ CS 13
+
+ Hesiod 13
+ HINFO 12
+ HS 13
+
+ IN 13
+ IN-ADDR.ARPA domain 22
+ Inverse queries 40
+
+ Mailbox names 47
+ MB 12
+ MD 12
+ MF 12
+ MG 12
+ MINFO 12
+ MINIMUM 20
+ MR 12
+ MX 12
+
+ NS 12
+ NULL 12
+
+ Port numbers 32
+ Primary server 5
+ PTR 12, 18
+
+
+
+Mockapetris [Page 54]
+
+RFC 1035 Domain Implementation and Specification November 1987
+
+
+ QCLASS 13
+ QTYPE 12
+
+ RDATA 12
+ RDLENGTH 11
+
+ Secondary server 5
+ SOA 12
+ Stub resolvers 7
+
+ TCP 32
+ TXT 12
+ TYPE 11
+
+ UDP 32
+
+ WKS 12
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Mockapetris [Page 55]
+
diff --git a/lib/dns/tests/testdata/master/master1.data b/lib/dns/tests/testdata/master/master1.data
new file mode 100644
index 0000000..030bc68
--- /dev/null
+++ b/lib/dns/tests/testdata/master/master1.data
@@ -0,0 +1,11 @@
+$TTL 1000
+@ in soa localhost. postmaster.localhost. (
+ 1993050801 ;serial
+ 3600 ;refresh
+ 1800 ;retry
+ 604800 ;expiration
+ 3600 ) ;minimum
+ in ns ns.vix.com.
+ in ns ns2.vix.com.
+ in ns ns3.vix.com.
+b in a 1.2.3.4
diff --git a/lib/dns/tests/testdata/master/master10.data b/lib/dns/tests/testdata/master/master10.data
new file mode 100644
index 0000000..9ee052f
--- /dev/null
+++ b/lib/dns/tests/testdata/master/master10.data
@@ -0,0 +1,7 @@
+;
+; the following black line contains spaces
+
+;
+@ 300 IN A 10.0.0.1
+ ;
+;
diff --git a/lib/dns/tests/testdata/master/master11.data b/lib/dns/tests/testdata/master/master11.data
new file mode 100644
index 0000000..0aaec25
--- /dev/null
+++ b/lib/dns/tests/testdata/master/master11.data
@@ -0,0 +1,6 @@
+;
+; The following serial number contains a leading 0 and a 9 so the
+; we can catch cases where it is incorrectly treated as a octal
+; number.
+;
+@ 300 IN SOA ns hostmaster 00090000 1200 3600 604800 300
diff --git a/lib/dns/tests/testdata/master/master12.data.in b/lib/dns/tests/testdata/master/master12.data.in
new file mode 100644
index 0000000..3634388
--- /dev/null
+++ b/lib/dns/tests/testdata/master/master12.data.in
@@ -0,0 +1 @@
+00000002000000004ed7306600000051000100060000000003e80000000100060474657374000035096c6f63616c686f7374000a706f73746d6173746572096c6f63616c686f73740076cb8ab100000e100000070800093a8000000e1000000046000100020000000003e8000000030006047465737400000c026e730376697803636f6d00000d036e73320376697803636f6d00000d036e73330376697803636f6d0000000022000100010000000003e80000000100080162047465737400000401020304
diff --git a/lib/dns/tests/testdata/master/master13.data.in b/lib/dns/tests/testdata/master/master13.data.in
new file mode 100644
index 0000000..d1c262f
--- /dev/null
+++ b/lib/dns/tests/testdata/master/master13.data.in
@@ -0,0 +1 @@
+00000002000000014ed7337f00000000000000000000000000000051000100060000000003e80000000100060474657374000035096c6f63616c686f7374000a706f73746d6173746572096c6f63616c686f73740076cb8ab100000e100000070800093a8000000e1000000046000100020000000003e8000000030006047465737400000c026e730376697803636f6d00000d036e73320376697803636f6d00000d036e73330376697803636f6d0000000022000100010000000003e80000000100080162047465737400000401020304
diff --git a/lib/dns/tests/testdata/master/master14.data.in b/lib/dns/tests/testdata/master/master14.data.in
new file mode 100644
index 0000000..149a25f
--- /dev/null
+++ b/lib/dns/tests/testdata/master/master14.data.in
@@ -0,0 +1 @@
+00000002000000014ed7337f0000000277df41e50000000000000051000100060000000003e80000000100060474657374000035096c6f63616c686f7374000a706f73746d6173746572096c6f63616c686f73740076cb8ab100000e100000070800093a8000000e1000000046000100020000000003e8000000030006047465737400000c026e730376697803636f6d00000d036e73320376697803636f6d00000d036e73330376697803636f6d0000000022000100010000000003e80000000100080162047465737400000401020304
diff --git a/lib/dns/tests/testdata/master/master15.data b/lib/dns/tests/testdata/master/master15.data
new file mode 100644
index 0000000..cf413ce
--- /dev/null
+++ b/lib/dns/tests/testdata/master/master15.data
@@ -0,0 +1,1609 @@
+$TTL 1000
+@ in soa localhost. postmaster.localhost. (
+ 1993050801 ;serial
+ 3600 ;refresh
+ 1800 ;retry
+ 604800 ;expiration
+ 3600 ) ;minimum
+ in ns ns.vix.com.
+ in ns ns2.vix.com.
+ in ns ns3.vix.com.
+b in a 1.2.3.4
+c in txt ( TOOBIGTOOBIGTOOBIGTOOBIGTOOBIGTOOBI
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890 )
diff --git a/lib/dns/tests/testdata/master/master16.data b/lib/dns/tests/testdata/master/master16.data
new file mode 100644
index 0000000..e969bd3
--- /dev/null
+++ b/lib/dns/tests/testdata/master/master16.data
@@ -0,0 +1,1609 @@
+$TTL 1000
+@ in soa localhost. postmaster.localhost. (
+ 1993050801 ;serial
+ 3600 ;refresh
+ 1800 ;retry
+ 604800 ;expiration
+ 3600 ) ;minimum
+ in ns ns.vix.com.
+ in ns ns2.vix.com.
+ in ns ns3.vix.com.
+b in a 1.2.3.4
+c in txt ( MAXSIZSEMAXSIZSEMAXSIZSEMAXSIZSMAX
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890
+ 1234567890123456789012345678901234567890 )
diff --git a/lib/dns/tests/testdata/master/master17.data b/lib/dns/tests/testdata/master/master17.data
new file mode 100644
index 0000000..4b2b63d
--- /dev/null
+++ b/lib/dns/tests/testdata/master/master17.data
@@ -0,0 +1,14 @@
+$ORIGIN test.
+$TTL 1000
+@ in soa localhost. postmaster.localhost. (
+ 1993050801 ;serial
+ 3600 ;refresh
+ 1800 ;retry
+ 604800 ;expiration
+ 3600 ) ;minimum
+ in ns ns.test.
+ in ns ns2.test.
+ in ns ns3.test.
+b in a 1.2.3.4
+$ORIGIN sub.test.
+ in a 4.3.2.1
diff --git a/lib/dns/tests/testdata/master/master18.data b/lib/dns/tests/testdata/master/master18.data
new file mode 100644
index 0000000..dddf04e
--- /dev/null
+++ b/lib/dns/tests/testdata/master/master18.data
@@ -0,0 +1,10 @@
+$TTL 1000
+@ in soa localhost. postmaster.localhost. (
+ 1993050801 ;serial
+ 3600 ;refresh
+ 1800 ;retry
+ 604800 ;expiration
+ 3600 ) ;minimum
+
+$INCLUDE "testkeys/Kexample.+008+20386.key";
+$INCLUDE "testkeys/Kexample.+008+37464.key";
diff --git a/lib/dns/tests/testdata/master/master2.data b/lib/dns/tests/testdata/master/master2.data
new file mode 100644
index 0000000..b8ca38d
--- /dev/null
+++ b/lib/dns/tests/testdata/master/master2.data
@@ -0,0 +1,11 @@
+$TTL 1000
+@ in soa localhost. postmaster.localhost. (
+ 1993050801 ;serial
+ 3600 ;refresh
+ 1800 ;retry
+ 604800 ;expiration
+ 3600 ) ;minimum
+a in ns
+a in ns ns2vix.com.
+a in ns ns3vix.com.
+b in a 1.2.3.4
diff --git a/lib/dns/tests/testdata/master/master3.data b/lib/dns/tests/testdata/master/master3.data
new file mode 100644
index 0000000..7283af6
--- /dev/null
+++ b/lib/dns/tests/testdata/master/master3.data
@@ -0,0 +1,11 @@
+$TTL 1000
+ in soa localhost. postmaster.localhost. (
+ 1993050801 ;serial
+ 3600 ;refresh
+ 1800 ;retry
+ 604800 ;expiration
+ 3600 ) ;minimum
+ in ns ns.vix.com
+ in ns ns2vix.com.
+a in ns ns3vix.com.
+b in a 1.2.3.4
diff --git a/lib/dns/tests/testdata/master/master4.data b/lib/dns/tests/testdata/master/master4.data
new file mode 100644
index 0000000..3a694ea
--- /dev/null
+++ b/lib/dns/tests/testdata/master/master4.data
@@ -0,0 +1,11 @@
+
+@ in soa localhost. postmaster.localhost. (
+ 1993050801 ;serial
+ 3600 ;refresh
+ 1800 ;retry
+ 604800 ;expiration
+ 3600 ) ;minimum
+a in ns ns.vix.com.
+a in ns ns2vix.com.
+a in ns ns3vix.com.
+b in a 1.2.3.4
diff --git a/lib/dns/tests/testdata/master/master5.data b/lib/dns/tests/testdata/master/master5.data
new file mode 100644
index 0000000..95234bd
--- /dev/null
+++ b/lib/dns/tests/testdata/master/master5.data
@@ -0,0 +1,11 @@
+$TTL 1000
+@ in soa localhost. postmaster.localhost. (
+ 1993050801 ;serial
+ 3600 ;refresh
+ 1800 ;retry
+ 604800 ;expiration
+ 3600 ) ;minimum
+a any ns ns.vix.com.
+a in ns ns2vix.com.
+a in ns ns3vix.com.
+b in a 1.2.3.4
diff --git a/lib/dns/tests/testdata/master/master6.data b/lib/dns/tests/testdata/master/master6.data
new file mode 100644
index 0000000..a9a37bb
--- /dev/null
+++ b/lib/dns/tests/testdata/master/master6.data
@@ -0,0 +1,33 @@
+
+$TTL 1000
+@ in soa localhost. postmaster.localhost. (
+ 1993050801 ;serial
+ 3600 ;refresh
+ 1800 ;retry
+ 604800 ;expiration
+ 3600 ) ;minimum
+
+secure1 3600 IN DNSKEY (
+ FLAG2|FLAG4|FLAG5|NTYP3|FLAG8|FLAG9|FLAG10|FLAG11|SIG15
+ 3 3
+ ArT0a8FtOZWEONG2YQVl9+RA34op30JPz4NPEroCxm2yImT2
+ 2OYggnPIzrgayyepgKU1PfTTypnJDTwrSrtISyEsj7tjM7/n
+ 03DP8VWSn0aLwpUuc7Sx9vtM1Wi+YeiA4Bv2Oz1VB9de4qql
+ sIq+KLn8J4wz95bGnJ0mHUB7oTDJ3Hl1zeaCMdX69Kr46yAY
+ AvGJJdGGDYxYgxzx2zNdzypkYSkxpdsNqUt38tabSfdvCn12
+ pnmSWjlVJsjHhsaYnrPhouN5acOXMNbxNVbGU5LZ8Es6EYbV
+ /7YMt8VUkA8/8UCszBBT7XAJ3OFjiMO8mvxrZZFzvwJlPBQ1
+ oFq/TNZlSe+N )
+
+secure2 3600 in DNSKEY (
+ flag2|flag4|flag5|ntyp3|flag8|flag9|flag10|flag11|sig15
+ 3 3
+ ArT0a8FtOZWEONG2YQVl9+RA34op30JPz4NPEroCxm2yImT2
+ 2OYggnPIzrgayyepgKU1PfTTypnJDTwrSrtISyEsj7tjM7/n
+ 03DP8VWSn0aLwpUuc7Sx9vtM1Wi+YeiA4Bv2Oz1VB9de4qql
+ sIq+KLn8J4wz95bGnJ0mHUB7oTDJ3Hl1zeaCMdX69Kr46yAY
+ AvGJJdGGDYxYgxzx2zNdzypkYSkxpdsNqUt38tabSfdvCn12
+ pnmSWjlVJsjHhsaYnrPhouN5acOXMNbxNVbGU5LZ8Es6EYbV
+ /7YMt8VUkA8/8UCszBBT7XAJ3OFjiMO8mvxrZZFzvwJlPBQ1
+ oFq/TNZlSe+N )
+
diff --git a/lib/dns/tests/testdata/master/master7.data b/lib/dns/tests/testdata/master/master7.data
new file mode 100644
index 0000000..2638b5d
--- /dev/null
+++ b/lib/dns/tests/testdata/master/master7.data
@@ -0,0 +1,17 @@
+
+$TTL 1000
+@ in soa localhost. postmaster.localhost. (
+ 1993050801 ;serial
+ 3600 ;refresh
+ 1800 ;retry
+ 604800 ;expiration
+ 3600 ) ;minimum
+
+secure1 3600 IN DNSKEY (
+ NOKEY|FLAG2|FLAG4|FLAG5|NTYP3|FLAG8|FLAG9|FLAG10|FLAG11|SIG15
+ 3 3 )
+
+secure2 3600 in DNSKEY (
+ nokey|flag2|flag4|flag5|ntyp3|flag8|flag9|flag10|flag11|sig15
+ 3 3 )
+
diff --git a/lib/dns/tests/testdata/master/master8.data b/lib/dns/tests/testdata/master/master8.data
new file mode 100644
index 0000000..2210f42
--- /dev/null
+++ b/lib/dns/tests/testdata/master/master8.data
@@ -0,0 +1,4 @@
+;
+; master7.data contains a good zone file
+;
+$include testdata/master/master7.data
diff --git a/lib/dns/tests/testdata/master/master9.data b/lib/dns/tests/testdata/master/master9.data
new file mode 100644
index 0000000..b22688b
--- /dev/null
+++ b/lib/dns/tests/testdata/master/master9.data
@@ -0,0 +1,4 @@
+;
+; master5.data is bad
+;
+$include testdata/master/master5.data
diff --git a/lib/dns/tests/testdata/nsec3/1024.db b/lib/dns/tests/testdata/nsec3/1024.db
new file mode 100644
index 0000000..2e7f182
--- /dev/null
+++ b/lib/dns/tests/testdata/nsec3/1024.db
@@ -0,0 +1,14 @@
+; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+;
+; This Source Code Form is subject to the terms of the Mozilla Public
+; License, v. 2.0. If a copy of the MPL was not distributed with this
+; file, You can obtain one at http://mozilla.org/MPL/2.0/.
+;
+; See the COPYRIGHT file distributed with this work for additional
+; information regarding copyright ownership.
+
+$TTL 0
+test. SOA . . 0 0 0 0 0
+test. NS .
+; 1024 bit key.
+test. IN DNSKEY 256 3 5 AwEAAd5oKx06HRE6NRrTDz49lljdRmxgp/4YB/cyMkpwUMkaLhDNCfTq hql84ab2LRbtUWLHFXGWENvxPGQzVHeleXu+3ThNfFOwIaySedxHmLGT lTtBRDhPc8iSb+2IYDemmA+ut8kwHhCVz/tDMbD/dgAswdOtmXCpQyJk Q1HqY3Xj
diff --git a/lib/dns/tests/testdata/nsec3/2048.db b/lib/dns/tests/testdata/nsec3/2048.db
new file mode 100644
index 0000000..eed399a
--- /dev/null
+++ b/lib/dns/tests/testdata/nsec3/2048.db
@@ -0,0 +1,14 @@
+; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+;
+; This Source Code Form is subject to the terms of the Mozilla Public
+; License, v. 2.0. If a copy of the MPL was not distributed with this
+; file, You can obtain one at http://mozilla.org/MPL/2.0/.
+;
+; See the COPYRIGHT file distributed with this work for additional
+; information regarding copyright ownership.
+
+$TTL 0
+test. SOA . . 0 0 0 0 0
+test. NS .
+; 2048 bits
+test. IN DNSKEY 256 3 5 AwEAAcfQX59iZr9gK+XzhTZQ5KWrfCLA0iYHTqheEIhC2dXS8gUSppQS g9SmzH2129u/LSSb7gqJSoLLAsn36iinqCqUXl2BT6xzwznbSP3mn0hn N6DegsykcYhHycKH6ifjZiMN+SGGeNsi5rhoW5Cj9ptw3C3yQnrFNDbS GZCT97z5lpQU3ZcvP4RDNk7dhri7Bh3SJeaCFoqx00NgFvlBR48hosSG bGUbUKzNf58GBTkW4Us2jIWsreZx8LLLev232Hy7NU9L19k+hVq7pJOf Uvtrn5fmGSutWOzsR+8EacOnh0lwssCKjutk5MSmfdFC5P7CTZkdq58L 8he13HGmr00=
diff --git a/lib/dns/tests/testdata/nsec3/4096.db b/lib/dns/tests/testdata/nsec3/4096.db
new file mode 100644
index 0000000..b5c8b7e
--- /dev/null
+++ b/lib/dns/tests/testdata/nsec3/4096.db
@@ -0,0 +1,14 @@
+; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+;
+; This Source Code Form is subject to the terms of the Mozilla Public
+; License, v. 2.0. If a copy of the MPL was not distributed with this
+; file, You can obtain one at http://mozilla.org/MPL/2.0/.
+;
+; See the COPYRIGHT file distributed with this work for additional
+; information regarding copyright ownership.
+
+$TTL 0
+test. SOA . . 0 0 0 0 0
+test. NS .
+; 4096 bits
+test. IN DNSKEY 256 3 5 AwEAAbYlqbKxXoq9mzkqdsAaSZ3XywBVAb2sCTgrQBCExyGEYNpWw3LN +imCrLQi7jHKQW6GZIqKNgQaiFEwr3zK8nPWbwNwyKU9a2hhINv/gim1 5iA87Vu7DiiJrQ0O79ospvsGsKknBQ41zaaQMp3Q/W1S6WNe4uyh4C/f R0qmxT+8MyXEqCpTGb+e+YT6BuqpNQPuYYYvUJ1/HJltzY/lY2b9RZ+Q ZJ23Zje79YIRM0kJapqj11fDUDeynhDL1DUikYCwRfQiO/blChhOHjIa uTK1qqRY3fqanLGOufpLTr7GRpL7RxeRIMJfDzmcjFLmCsMA1AJ56Bxq jiXr3ODgn9D30vAB74Lr7lqLQSWyrSlJjoZLLhmPrEP/nnuCxEhOhDRA XJpJWpcQ4Hdu+yb5K/qldnsGLLI1Hr0GmhLTDHsxDb6BxM7/8rv8QeQY GKSGshBqD2lO1xUVT8inbi8uXI1iyN68vHX6xoFT5wsjls70PxSZPO5i F40vn6BWNsHtKWOCDqMKYx8hYwiv0zETVwxBaj58vylFwYGU+g1wIQmF Pgi2HKv4KaxgikUvdFISre5rxVoG5VrmmXWiNJcLTbwZ+tE1xujCNU1c V31CaIB5hdSnkEvQADr5V64RTxWAKuSLNMU+XUqTkaJHasSm3OPJOteo SPj2uoesuxNFYps3
diff --git a/lib/dns/tests/testdata/nsec3/min-1024.db b/lib/dns/tests/testdata/nsec3/min-1024.db
new file mode 100644
index 0000000..7d0f95c
--- /dev/null
+++ b/lib/dns/tests/testdata/nsec3/min-1024.db
@@ -0,0 +1,18 @@
+; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+;
+; This Source Code Form is subject to the terms of the Mozilla Public
+; License, v. 2.0. If a copy of the MPL was not distributed with this
+; file, You can obtain one at http://mozilla.org/MPL/2.0/.
+;
+; See the COPYRIGHT file distributed with this work for additional
+; information regarding copyright ownership.
+
+$TTL 0
+test. SOA . . 0 0 0 0 0
+test. NS .
+; 1024 bit key.
+test. IN DNSKEY 256 3 5 AwEAAd5oKx06HRE6NRrTDz49lljdRmxgp/4YB/cyMkpwUMkaLhDNCfTq hql84ab2LRbtUWLHFXGWENvxPGQzVHeleXu+3ThNfFOwIaySedxHmLGT lTtBRDhPc8iSb+2IYDemmA+ut8kwHhCVz/tDMbD/dgAswdOtmXCpQyJk Q1HqY3Xj
+; 2048 bits
+test. IN DNSKEY 256 3 5 AwEAAcfQX59iZr9gK+XzhTZQ5KWrfCLA0iYHTqheEIhC2dXS8gUSppQS g9SmzH2129u/LSSb7gqJSoLLAsn36iinqCqUXl2BT6xzwznbSP3mn0hn N6DegsykcYhHycKH6ifjZiMN+SGGeNsi5rhoW5Cj9ptw3C3yQnrFNDbS GZCT97z5lpQU3ZcvP4RDNk7dhri7Bh3SJeaCFoqx00NgFvlBR48hosSG bGUbUKzNf58GBTkW4Us2jIWsreZx8LLLev232Hy7NU9L19k+hVq7pJOf Uvtrn5fmGSutWOzsR+8EacOnh0lwssCKjutk5MSmfdFC5P7CTZkdq58L 8he13HGmr00=
+; 4096 bits
+test. IN DNSKEY 256 3 5 AwEAAbYlqbKxXoq9mzkqdsAaSZ3XywBVAb2sCTgrQBCExyGEYNpWw3LN +imCrLQi7jHKQW6GZIqKNgQaiFEwr3zK8nPWbwNwyKU9a2hhINv/gim1 5iA87Vu7DiiJrQ0O79ospvsGsKknBQ41zaaQMp3Q/W1S6WNe4uyh4C/f R0qmxT+8MyXEqCpTGb+e+YT6BuqpNQPuYYYvUJ1/HJltzY/lY2b9RZ+Q ZJ23Zje79YIRM0kJapqj11fDUDeynhDL1DUikYCwRfQiO/blChhOHjIa uTK1qqRY3fqanLGOufpLTr7GRpL7RxeRIMJfDzmcjFLmCsMA1AJ56Bxq jiXr3ODgn9D30vAB74Lr7lqLQSWyrSlJjoZLLhmPrEP/nnuCxEhOhDRA XJpJWpcQ4Hdu+yb5K/qldnsGLLI1Hr0GmhLTDHsxDb6BxM7/8rv8QeQY GKSGshBqD2lO1xUVT8inbi8uXI1iyN68vHX6xoFT5wsjls70PxSZPO5i F40vn6BWNsHtKWOCDqMKYx8hYwiv0zETVwxBaj58vylFwYGU+g1wIQmF Pgi2HKv4KaxgikUvdFISre5rxVoG5VrmmXWiNJcLTbwZ+tE1xujCNU1c V31CaIB5hdSnkEvQADr5V64RTxWAKuSLNMU+XUqTkaJHasSm3OPJOteo SPj2uoesuxNFYps3
diff --git a/lib/dns/tests/testdata/nsec3/min-2048.db b/lib/dns/tests/testdata/nsec3/min-2048.db
new file mode 100644
index 0000000..027c0b9
--- /dev/null
+++ b/lib/dns/tests/testdata/nsec3/min-2048.db
@@ -0,0 +1,16 @@
+; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+;
+; This Source Code Form is subject to the terms of the Mozilla Public
+; License, v. 2.0. If a copy of the MPL was not distributed with this
+; file, You can obtain one at http://mozilla.org/MPL/2.0/.
+;
+; See the COPYRIGHT file distributed with this work for additional
+; information regarding copyright ownership.
+
+$TTL 0
+test. SOA . . 0 0 0 0 0
+test. NS .
+; 2048 bits
+test. IN DNSKEY 256 3 5 AwEAAcfQX59iZr9gK+XzhTZQ5KWrfCLA0iYHTqheEIhC2dXS8gUSppQS g9SmzH2129u/LSSb7gqJSoLLAsn36iinqCqUXl2BT6xzwznbSP3mn0hn N6DegsykcYhHycKH6ifjZiMN+SGGeNsi5rhoW5Cj9ptw3C3yQnrFNDbS GZCT97z5lpQU3ZcvP4RDNk7dhri7Bh3SJeaCFoqx00NgFvlBR48hosSG bGUbUKzNf58GBTkW4Us2jIWsreZx8LLLev232Hy7NU9L19k+hVq7pJOf Uvtrn5fmGSutWOzsR+8EacOnh0lwssCKjutk5MSmfdFC5P7CTZkdq58L 8he13HGmr00=
+; 4096 bits
+test. IN DNSKEY 256 3 5 AwEAAbYlqbKxXoq9mzkqdsAaSZ3XywBVAb2sCTgrQBCExyGEYNpWw3LN +imCrLQi7jHKQW6GZIqKNgQaiFEwr3zK8nPWbwNwyKU9a2hhINv/gim1 5iA87Vu7DiiJrQ0O79ospvsGsKknBQ41zaaQMp3Q/W1S6WNe4uyh4C/f R0qmxT+8MyXEqCpTGb+e+YT6BuqpNQPuYYYvUJ1/HJltzY/lY2b9RZ+Q ZJ23Zje79YIRM0kJapqj11fDUDeynhDL1DUikYCwRfQiO/blChhOHjIa uTK1qqRY3fqanLGOufpLTr7GRpL7RxeRIMJfDzmcjFLmCsMA1AJ56Bxq jiXr3ODgn9D30vAB74Lr7lqLQSWyrSlJjoZLLhmPrEP/nnuCxEhOhDRA XJpJWpcQ4Hdu+yb5K/qldnsGLLI1Hr0GmhLTDHsxDb6BxM7/8rv8QeQY GKSGshBqD2lO1xUVT8inbi8uXI1iyN68vHX6xoFT5wsjls70PxSZPO5i F40vn6BWNsHtKWOCDqMKYx8hYwiv0zETVwxBaj58vylFwYGU+g1wIQmF Pgi2HKv4KaxgikUvdFISre5rxVoG5VrmmXWiNJcLTbwZ+tE1xujCNU1c V31CaIB5hdSnkEvQADr5V64RTxWAKuSLNMU+XUqTkaJHasSm3OPJOteo SPj2uoesuxNFYps3
diff --git a/lib/dns/tests/testdata/zt/zone1.db b/lib/dns/tests/testdata/zt/zone1.db
new file mode 100644
index 0000000..8286d88
--- /dev/null
+++ b/lib/dns/tests/testdata/zt/zone1.db
@@ -0,0 +1,20 @@
+; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+;
+; This Source Code Form is subject to the terms of the Mozilla Public
+; License, v. 2.0. If a copy of the MPL was not distributed with this
+; file, You can obtain one at http://mozilla.org/MPL/2.0/.
+;
+; See the COPYRIGHT file distributed with this work for additional
+; information regarding copyright ownership.
+
+$TTL 1000
+@ in soa localhost. postmaster.localhost. (
+ 1993050801 ;serial
+ 3600 ;refresh
+ 1800 ;retry
+ 604800 ;expiration
+ 3600 ) ;minimum
+ in ns ns.vix.com.
+ in ns ns2.vix.com.
+ in ns ns3.vix.com.
+a in a 1.2.3.4
diff --git a/lib/dns/tests/testkeys/Kexample.+008+20386.key b/lib/dns/tests/testkeys/Kexample.+008+20386.key
new file mode 100644
index 0000000..3404dca
--- /dev/null
+++ b/lib/dns/tests/testkeys/Kexample.+008+20386.key
@@ -0,0 +1,5 @@
+; This is a key-signing key, keyid 20386, for example.
+; Created: 20000101000000 (Sat Jan 1 00:00:00 2000)
+; Publish: 20000101000000 (Sat Jan 1 00:00:00 2000)
+; Activate: 20000101000000 (Sat Jan 1 00:00:00 2000)
+example. IN DNSKEY 257 3 8 AwEAAZd7/hBRvMooz0sepkD/2r3Bp021f8lGzDj6sZEVbg1hcqZTzURc eGkS541wyOqjvJv2KBi5qLLE2HthmexmOBycjTQ7EiKd1P9bE8RgF8Et j73X/CHLiX6YL7cb93TXWiUvbRh4E6D2URgOmxMdMOXTuCvjvDaGVCOt Jc77UUosuBeurZzP8g8t/zccAUTzu2cdRyI5/ZxOBfJaDtc9TlRdWsaN Af+nT0C14ccH7QVlKjjaYV4lXueruDW3yTTzu9bQ1ikgegsCLi/tcD/1 dWTOI9whV06szs+ouhuJkZuhIjrGDtOHCpjPjIxOOrIZceU1YSY30kAR QNVzshJqyx8=
diff --git a/lib/dns/tests/testkeys/Kexample.+008+20386.private b/lib/dns/tests/testkeys/Kexample.+008+20386.private
new file mode 100644
index 0000000..d8cff93
--- /dev/null
+++ b/lib/dns/tests/testkeys/Kexample.+008+20386.private
@@ -0,0 +1,13 @@
+Private-key-format: v1.3
+Algorithm: 8 (RSASHA256)
+Modulus: l3v+EFG8yijPSx6mQP/avcGnTbV/yUbMOPqxkRVuDWFyplPNRFx4aRLnjXDI6qO8m/YoGLmossTYe2GZ7GY4HJyNNDsSIp3U/1sTxGAXwS2Pvdf8IcuJfpgvtxv3dNdaJS9tGHgToPZRGA6bEx0w5dO4K+O8NoZUI60lzvtRSiy4F66tnM/yDy3/NxwBRPO7Zx1HIjn9nE4F8loO1z1OVF1axo0B/6dPQLXhxwftBWUqONphXiVe56u4NbfJNPO71tDWKSB6CwIuL+1wP/V1ZM4j3CFXTqzOz6i6G4mRm6EiOsYO04cKmM+MjE46shlx5TVhJjfSQBFA1XOyEmrLHw==
+PublicExponent: AQAB
+PrivateExponent: aSkynrGfldfuz/9e+xCjEcg2FMRDCb+UVpnyWv29gJx9sunKPgLTtF3jUVVSpVE1xi+EdmWsry3n+v8uk+YCXhpwDCpV1KItE3huqIzs8LZoaypdZjieIrwTo9JOX1aAxf++hJYXSk60zTaWgRZqs6He4Nkf99oY3wt8i8v8CrkfQy76K/qK9xUVv5GHrEZzCGLfLv77eqDab/J84ANxc0kUtQvgt2/JTHofXmcA6/YDh5PWB8KRw1PjQTck61/xIgfI6ky/yIF1riCQCYXwTv7jcmMV/QvQ+dfN+HZ2CSGp7xcH2Yxe9OhAY823ZkmkOQ2YZPjIj6dEoRMmSiaagQ==
+Prime1: x2GMnpRPwvUhM+yPRa7nh5Jjl4mbofeOtVrxe1hEVy8l2UGFh+FDZCbyoLRNUTYDji00NHpGtmcAyoY9pLdOn7ci4zqGVnNJcIY75Ie4p6J7pPfDh9d+AGtJ5NpNhr1sjD0bFncJC2FGY9vj4eC0CkatMu/Qovrd2FwZ8VpDsAk=
+Prime2: woB8MYsEfSYGD0hZGtmgK6UQ+Oo9smxdPmahLYXnLSAdqtqZbZX+ABk/kFduT+XwlHOXmp3HMmUtQTRZBaQyBrsFWfWjOGevByEsT9aLQSZOEgnqy4xrc9XNwDs4/WkrEgw/TOVnZYdaCyLxsFl4bpTX8Fj3yVqg/tJvuUMWG+c=
+Exponent1: iQO7a9rF+VcVSyZ8yslIaL0r3Z5+Kk8CbhSiMD5XMIbA/sztI5SlCDVPtSpSm8V/qfvcjVeeMokUXRjlUcV6rX1f50F3wf8V79L/Y6v1NJYPXC273CU1fLo+HJv8fOS9rJ3teIGy4HQnuEYLE1WkxA8PxRpSiT3WqHGajmaWb2k=
+Exponent2: elMWSI5Wz2KXkwr8Rz+xVWGl7/ZZwRoX9oPTQG8jeiTlo6uBrQMVUPiQGnZyQTuq96JPKYWrXs11DbofdsXSVJtQfUhYU8QZtxEs7jVPNTUjCoNEMKnqdlpz4T8d03pOBTbApNruEVNz1OcwO6m5bUqdGGLLy838zOaKL2i6wec=
+Coefficient: q2mejAmT3A4H2C0rT1hm8XQFuISHjAAEyM9t09Q8tEeQ0lHi4gMVA3bXoAn9U21eBkFQDwvyB0vqlVSGgRqHovOKx9uXAU9eoDxGcJsFlGsM0aUsUjGVXv5kVmaw8a5PHBbvYAbgAZUmKqrVF0PWD3o+/DbzP9PCmlJcqxoAulU=
+Created: 20000101000000
+Publish: 20000101000000
+Activate: 20000101000000
diff --git a/lib/dns/tests/testkeys/Kexample.+008+37464.key b/lib/dns/tests/testkeys/Kexample.+008+37464.key
new file mode 100644
index 0000000..3dd0619
--- /dev/null
+++ b/lib/dns/tests/testkeys/Kexample.+008+37464.key
@@ -0,0 +1,5 @@
+; This is a zone-signing key, keyid 37464, for example.
+; Created: 20000101000000 (Sat Jan 1 00:00:00 2000)
+; Publish: 20000101000000 (Sat Jan 1 00:00:00 2000)
+; Activate: 20000101000000 (Sat Jan 1 00:00:00 2000)
+example. IN DNSKEY 256 3 8 AwEAAbxHOF8G0xw9ekCodhL8KivuZ3o0jmGlycLiXBjBN8c5R5fjLjUh D0gy3IDbDC+kLaPhHGF/MwrSEjrgSowxZ8nrxDzsq5ZdpeUsYaNrbQEY /mqf35T/9/Ulm4v06x58v/NTugWd05Xq04aAyfm7EViyGFzmVOVfPnll h9xQtvWEWoRWPseFw+dY5/nc/+xB/IsQMihoH2rO+cek/lsP3R9DsHCG RbQ/ks/+rrp6/O+QJZyZrzsONl7mlMDXNy3Pz9J4qMW2W6Mz702LN324 7/9UsetDGGbuZfrCLMpKWXzdsJm36DOk4aMooS9111plfXaXQgQNcL5G 021utpTau+8=
diff --git a/lib/dns/tests/testkeys/Kexample.+008+37464.private b/lib/dns/tests/testkeys/Kexample.+008+37464.private
new file mode 100644
index 0000000..ecc2ad0
--- /dev/null
+++ b/lib/dns/tests/testkeys/Kexample.+008+37464.private
@@ -0,0 +1,13 @@
+Private-key-format: v1.3
+Algorithm: 8 (RSASHA256)
+Modulus: vEc4XwbTHD16QKh2EvwqK+5nejSOYaXJwuJcGME3xzlHl+MuNSEPSDLcgNsML6Qto+EcYX8zCtISOuBKjDFnyevEPOyrll2l5Sxho2ttARj+ap/flP/39SWbi/TrHny/81O6BZ3TlerThoDJ+bsRWLIYXOZU5V8+eWWH3FC29YRahFY+x4XD51jn+dz/7EH8ixAyKGgfas75x6T+Ww/dH0OwcIZFtD+Sz/6uunr875AlnJmvOw42XuaUwNc3Lc/P0nioxbZbozPvTYs3fbjv/1Sx60MYZu5l+sIsykpZfN2wmbfoM6ThoyihL3XXWmV9dpdCBA1wvkbTbW62lNq77w==
+PublicExponent: AQAB
+PrivateExponent: AhR3VvVoV6OGOjiiNUt728hidEMoX4PJWtHNWqinyRek5tSnqgaXeKC3NuU0mUIjDvBps9oH4lK3yNa5fBr/nodwP4wNyTd3obR/z6JcLersxJjHi4nYX2ju8vjdsBSIulNudqlrsPhLJe0+Tff3FRfClSQmQ/JtakHo4lIx8zxiOJY8aWFeHGdWJDkAf6NStt3eVYyOyAwISfv3muaGPZKShiIOfLyTvqFqzwYFgdTWmvFqTdwgjIMc5XAwqw73WP2BPCN+fdCiMtrw0fCrhWzw/gfMJBHdOPH0diUZysAJhM0vdVKQzEi/g3YOo00fahZiPzaxNtZnLNj2mA54YQ==
+Prime1: 5YpfVjEtL1owW9gSFbIMx65POr+fiktxirgy1bc5fSsVqUgG6zhbaN/VpWcNZG0Zg5xd6S7C8V3djGlnJN8wZIyjIh7+Z3WWjqbOD9oY7rC1fR+W0OvbCmZiEzOpRJ5qoMOh1MzkkanhMy0/ICpaa8eQ9zEb80oTIQpFgoLn7K0=
+Prime2: 0fs3ncL5/2qzq2dmPXLYcOfc1EGSuESO0VpREP8EpTkyPKeVw5LaF9TgZRqPWlRf2T0LPoZ766xLAn090u0pLQ5fWM96NMas7kS+rxtRssat6MiQo3YfoU3ysk3xuPzrMBHyn/N42CjSG+bJEToHR7V16KsCT6dBIPkI3tj/Yos=
+Exponent1: Bdsp44ENrg+W/EDe9T69pLqFuvH4mAaktu1MHre198OJoe/8fTPK4ToUsUuXw+Akrn7mxnQy9QV4CYUG5KHtEiOkZdJ0mx8c4DbROwZNbImFl9OefWYHCJTkG6lNwDpqbf+PuWYgzraO0EdvPNrXw7grsqLGG8bgBg/FBjdgw2E=
+Exponent2: uV1pxW0fwGhzX3aR/ODrTRCCEyYn3V84LHvsYHKfqTOKs5zFSrbSrIMR7G676ePeESogSPvzXSLlvLbO4urVlJ7BcOcHXJuegWBSbMZTItzdHUgg1wwp8/2Zp+nC36j1/aN6adVG8ptmj5b2HKz7TERWaCS+j454oiD1wbQSDu0=
+Coefficient: JO6RxBIaoEd/Z4ITcsYT8TslP1KmIuAqdhMt3FSpqeogUDut7f3FZIEyNi4wsrSK5peIQSVmO2pQLupS+eRIPHXZ1vh5kcFAsgd7XBb7Fvsg26/WSjhB4wjx+wgWzVomK0519pfdtH854fePWPkdDKtLNL2zh0APne3GjwrbNEM=
+Created: 20000101000000
+Publish: 20000101000000
+Activate: 20000101000000
diff --git a/lib/dns/tests/time_test.c b/lib/dns/tests/time_test.c
new file mode 100644
index 0000000..28b72b6
--- /dev/null
+++ b/lib/dns/tests/time_test.c
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <atf-c.h>
+
+#include <inttypes.h>
+#include <unistd.h>
+
+#include <dns/time.h>
+
+#include "dnstest.h"
+
+#define TEST_ORIGIN "test"
+
+/*
+ * Individual unit tests
+ */
+
+/* value = 0xfffffffff <-> 19691231235959 */
+ATF_TC(epoch_minus_one);
+ATF_TC_HEAD(epoch_minus_one, tc) {
+ atf_tc_set_md_var(tc, "descr", "0xffffffff <-> 19691231235959");
+}
+ATF_TC_BODY(epoch_minus_one, tc) {
+ const char *test_text = "19691231235959";
+ const uint32_t test_time = 0xffffffff;
+ isc_result_t result;
+ isc_buffer_t target;
+ uint32_t when;
+ char buf[128];
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ memset(buf, 0, sizeof(buf));
+ isc_buffer_init(&target, buf, sizeof(buf));
+ result = dns_time32_totext(test_time, &target);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ ATF_REQUIRE_STREQ(buf, test_text);
+ result = dns_time32_fromtext(test_text, &when);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ ATF_REQUIRE_EQ(when, test_time);
+ dns_test_end();
+}
+
+/* value = 0x000000000 <-> 19700101000000*/
+ATF_TC(epoch);
+ATF_TC_HEAD(epoch, tc) {
+ atf_tc_set_md_var(tc, "descr", "0x00000000 <-> 19700101000000");
+}
+ATF_TC_BODY(epoch, tc) {
+ const char *test_text = "19700101000000";
+ const uint32_t test_time = 0x00000000;
+ isc_result_t result;
+ isc_buffer_t target;
+ uint32_t when;
+ char buf[128];
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ memset(buf, 0, sizeof(buf));
+ isc_buffer_init(&target, buf, sizeof(buf));
+ result = dns_time32_totext(test_time, &target);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ ATF_REQUIRE_STREQ(buf, test_text);
+ result = dns_time32_fromtext(test_text, &when);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ ATF_REQUIRE_EQ(when, test_time);
+ dns_test_end();
+}
+
+/* value = 0x7fffffff <-> 20380119031407 */
+ATF_TC(half_maxint);
+ATF_TC_HEAD(half_maxint, tc) {
+ atf_tc_set_md_var(tc, "descr", "0x7fffffff <-> 20380119031407");
+}
+ATF_TC_BODY(half_maxint, tc) {
+ const char *test_text = "20380119031407";
+ const uint32_t test_time = 0x7fffffff;
+ isc_result_t result;
+ isc_buffer_t target;
+ uint32_t when;
+ char buf[128];
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ memset(buf, 0, sizeof(buf));
+ isc_buffer_init(&target, buf, sizeof(buf));
+ result = dns_time32_totext(test_time, &target);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ ATF_REQUIRE_STREQ(buf, test_text);
+ result = dns_time32_fromtext(test_text, &when);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ ATF_REQUIRE_EQ(when, test_time);
+ dns_test_end();
+}
+
+/* value = 0x80000000 <-> 20380119031408 */
+ATF_TC(half_plus_one);
+ATF_TC_HEAD(half_plus_one, tc) {
+ atf_tc_set_md_var(tc, "descr", "0x80000000 <-> 20380119031408");
+}
+ATF_TC_BODY(half_plus_one, tc) {
+ const char *test_text = "20380119031408";
+ const uint32_t test_time = 0x80000000;
+ isc_result_t result;
+ isc_buffer_t target;
+ uint32_t when;
+ char buf[128];
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ memset(buf, 0, sizeof(buf));
+ isc_buffer_init(&target, buf, sizeof(buf));
+ result = dns_time32_totext(test_time, &target);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ ATF_REQUIRE_STREQ(buf, test_text);
+ result = dns_time32_fromtext(test_text, &when);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ ATF_REQUIRE_EQ(when, test_time);
+ dns_test_end();
+}
+
+/* value = 0xef68f5d0 <-> 19610307130000 */
+ATF_TC(fifty_before);
+ATF_TC_HEAD(fifty_before, tc) {
+ atf_tc_set_md_var(tc, "descr", "0xef68f5d0 <-> 19610307130000");
+}
+ATF_TC_BODY(fifty_before, tc) {
+ isc_result_t result;
+ const char *test_text = "19610307130000";
+ const uint32_t test_time = 0xef68f5d0;
+ isc_buffer_t target;
+ uint32_t when;
+ char buf[128];
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ memset(buf, 0, sizeof(buf));
+ isc_buffer_init(&target, buf, sizeof(buf));
+ result = dns_time32_totext(test_time, &target);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ ATF_REQUIRE_STREQ(buf, test_text);
+ result = dns_time32_fromtext(test_text, &when);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ ATF_REQUIRE_EQ(when, test_time);
+ dns_test_end();
+}
+
+/* value = 0x4d74d6d0 <-> 20110307130000 */
+ATF_TC(some_ago);
+ATF_TC_HEAD(some_ago, tc) {
+ atf_tc_set_md_var(tc, "descr", "0x4d74d6d0 <-> 20110307130000");
+}
+ATF_TC_BODY(some_ago, tc) {
+ const char *test_text = "20110307130000";
+ const uint32_t test_time = 0x4d74d6d0;
+ isc_result_t result;
+ isc_buffer_t target;
+ uint32_t when;
+ char buf[128];
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ memset(buf, 0, sizeof(buf));
+ isc_buffer_init(&target, buf, sizeof(buf));
+ result = dns_time32_totext(test_time, &target);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ ATF_REQUIRE_STREQ(buf, test_text);
+ result = dns_time32_fromtext(test_text, &when);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ ATF_REQUIRE_EQ(when, test_time);
+ dns_test_end();
+}
+
+/*
+ * Main
+ */
+ATF_TP_ADD_TCS(tp) {
+ ATF_TP_ADD_TC(tp, epoch_minus_one);
+ ATF_TP_ADD_TC(tp, epoch);
+ ATF_TP_ADD_TC(tp, half_maxint);
+ ATF_TP_ADD_TC(tp, half_plus_one);
+ ATF_TP_ADD_TC(tp, fifty_before);
+ ATF_TP_ADD_TC(tp, some_ago);
+
+ return (atf_no_error());
+}
+
diff --git a/lib/dns/tests/tsig_test.c b/lib/dns/tests/tsig_test.c
new file mode 100644
index 0000000..443fb36
--- /dev/null
+++ b/lib/dns/tests/tsig_test.c
@@ -0,0 +1,491 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* ! \file */
+
+#include <config.h>
+#include <atf-c.h>
+
+#include <isc/mem.h>
+#include <isc/print.h>
+
+#include <dns/rdatalist.h>
+#include <dns/rdataset.h>
+#include <dns/tsig.h>
+
+#include "dnstest.h"
+
+
+static int debug = 0;
+
+static isc_result_t
+add_mac(dst_context_t *tsigctx, isc_buffer_t *buf) {
+ dns_rdata_any_tsig_t tsig;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_buffer_t databuf;
+ isc_region_t r;
+ isc_result_t result;
+ unsigned char tsigbuf[1024];
+
+ isc_buffer_usedregion(buf, &r);
+ dns_rdata_fromregion(&rdata, dns_rdataclass_any,
+ dns_rdatatype_tsig, &r);
+ isc_buffer_init(&databuf, tsigbuf, sizeof(tsigbuf));
+ CHECK(dns_rdata_tostruct(&rdata, &tsig, NULL));
+ isc_buffer_putuint16(&databuf, tsig.siglen);
+ isc_buffer_putmem(&databuf, tsig.signature, tsig.siglen);
+ isc_buffer_usedregion(&databuf, &r);
+ result = dst_context_adddata(tsigctx, &r);
+ dns_rdata_freestruct(&tsig);
+ cleanup:
+ return (result);
+}
+
+static isc_result_t
+add_tsig(dst_context_t *tsigctx, dns_tsigkey_t *key, isc_buffer_t *target) {
+ dns_compress_t cctx;
+ dns_rdata_any_tsig_t tsig;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdatalist_t rdatalist;
+ dns_rdataset_t rdataset;
+ isc_buffer_t *dynbuf = NULL;
+ isc_buffer_t databuf;
+ isc_buffer_t sigbuf;
+ isc_region_t r;
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_stdtime_t now;
+ unsigned char tsigbuf[1024];
+ unsigned int count;
+ unsigned int sigsize = 0;
+ bool invalidate_ctx = false;
+
+ memset(&tsig, 0, sizeof(tsig));
+
+ CHECK(dns_compress_init(&cctx, -1, mctx));
+ invalidate_ctx = true;
+
+ 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;
+ tsig.fudge = DNS_TSIG_FUDGE;
+ tsig.originalid = 50;
+ tsig.error = dns_rcode_noerror;
+ tsig.otherlen = 0;
+ tsig.other = NULL;
+
+ isc_buffer_init(&databuf, tsigbuf, sizeof(tsigbuf));
+ isc_buffer_putuint48(&databuf, tsig.timesigned);
+ isc_buffer_putuint16(&databuf, tsig.fudge);
+ isc_buffer_usedregion(&databuf, &r);
+ CHECK(dst_context_adddata(tsigctx, &r));
+
+ CHECK(dst_key_sigsize(key->key, &sigsize));
+ tsig.signature = (unsigned char *) isc_mem_get(mctx, sigsize);
+ if (tsig.signature == NULL)
+ CHECK(ISC_R_NOMEMORY);
+ isc_buffer_init(&sigbuf, tsig.signature, sigsize);
+ CHECK(dst_context_sign(tsigctx, &sigbuf));
+ tsig.siglen = isc_buffer_usedlength(&sigbuf);
+ ATF_CHECK_EQ(sigsize, tsig.siglen);
+
+ CHECK(isc_buffer_allocate(mctx, &dynbuf, 512));
+ CHECK(dns_rdata_fromstruct(&rdata, dns_rdataclass_any,
+ dns_rdatatype_tsig, &tsig, dynbuf));
+ dns_rdatalist_init(&rdatalist);
+ rdatalist.rdclass = dns_rdataclass_any;
+ rdatalist.type = dns_rdatatype_tsig;
+ ISC_LIST_APPEND(rdatalist.rdata, &rdata, link);
+ dns_rdataset_init(&rdataset);
+ CHECK(dns_rdatalist_tordataset(&rdatalist, &rdataset));
+ CHECK(dns_rdataset_towire(&rdataset, &key->name, &cctx,
+ target, 0, &count));
+
+ /*
+ * Fixup additional record count.
+ */
+ ((unsigned char*)target->base)[11]++;
+ if (((unsigned char*)target->base)[11] == 0)
+ ((unsigned char*)target->base)[10]++;
+ cleanup:
+ if (tsig.signature != NULL)
+ isc_mem_put(mctx, tsig.signature, sigsize);
+ if (dynbuf != NULL)
+ isc_buffer_free(&dynbuf);
+ if (invalidate_ctx)
+ dns_compress_invalidate(&cctx);
+
+ return (result);
+}
+
+static void
+printmessage(dns_message_t *msg) {
+ isc_buffer_t b;
+ char *buf = NULL;
+ int len = 1024;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ if (!debug)
+ return;
+
+ do {
+ buf = isc_mem_get(mctx, len);
+ if (buf == NULL)
+ return;
+
+ isc_buffer_init(&b, buf, len);
+ result = dns_message_totext(msg, &dns_master_style_debug,
+ 0, &b);
+ if (result == ISC_R_NOSPACE) {
+ isc_mem_put(mctx, buf, len);
+ len *= 2;
+ } else if (result == ISC_R_SUCCESS)
+ printf("%.*s\n", (int) isc_buffer_usedlength(&b), buf);
+ } while (result == ISC_R_NOSPACE);
+
+ if (buf != NULL)
+ isc_mem_put(mctx, buf, len);
+}
+
+static void
+render(isc_buffer_t *buf, unsigned flags, dns_tsigkey_t *key,
+ isc_buffer_t **tsigin, isc_buffer_t **tsigout,
+ dst_context_t *tsigctx)
+{
+ dns_message_t *msg = NULL;
+ dns_compress_t cctx;
+ isc_result_t result;
+
+ result = dns_message_create(mctx, DNS_MESSAGE_INTENTRENDER, &msg);
+ ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS,
+ "dns_message_create: %s",
+ dns_result_totext(result));
+ ATF_REQUIRE(msg != NULL);
+
+ msg->id = 50;
+ msg->rcode = dns_rcode_noerror;
+ msg->flags = flags;
+
+ /*
+ * XXXMPA: this hack needs to be replaced with use of
+ * dns_message_reply() at some point.
+ */
+ if ((flags & DNS_MESSAGEFLAG_QR) != 0)
+ msg->verified_sig = 1;
+
+ if (tsigin == tsigout)
+ msg->tcp_continuation = 1;
+
+ if (tsigctx == NULL) {
+ result = dns_message_settsigkey(msg, key);
+ ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS,
+ "dns_message_settsigkey: %s",
+ dns_result_totext(result));
+
+ result = dns_message_setquerytsig(msg, *tsigin);
+ ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS,
+ "dns_message_setquerytsig: %s",
+ dns_result_totext(result));
+ }
+
+ result = dns_compress_init(&cctx, -1, mctx);
+ ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS,
+ "dns_compress_init: %s",
+ dns_result_totext(result));
+
+ result = dns_message_renderbegin(msg, &cctx, buf);
+ ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS,
+ "dns_message_renderbegin: %s",
+ dns_result_totext(result));
+
+ result = dns_message_renderend(msg);
+ ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS,
+ "dns_message_renderend: %s",
+ dns_result_totext(result));
+
+ if (tsigctx != NULL) {
+ isc_region_t r;
+
+ isc_buffer_usedregion(buf, &r);
+ result = dst_context_adddata(tsigctx, &r);
+ ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS,
+ "dst_context_adddata: %s",
+ dns_result_totext(result));
+ } else {
+ if (tsigin == tsigout && *tsigin != NULL)
+ isc_buffer_free(tsigin);
+
+ result = dns_message_getquerytsig(msg, mctx, tsigout);
+ ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS,
+ "dns_message_getquerytsig: %s",
+ dns_result_totext(result));
+ }
+
+ dns_compress_invalidate(&cctx);
+ dns_message_destroy(&msg);
+}
+
+/*
+ * Check that a simulated three message TCP sequence where the first
+ * and last messages contain TSIGs but the intermediate message doesn't
+ * correctly verifies.
+ */
+ATF_TC(tsig_tcp);
+ATF_TC_HEAD(tsig_tcp, tc) {
+ atf_tc_set_md_var(tc, "descr", "test tsig tcp-continuation validation");
+}
+ATF_TC_BODY(tsig_tcp, tc) {
+ dns_name_t *tsigowner = NULL;
+ dns_fixedname_t fkeyname;
+ dns_message_t *msg = NULL;
+ dns_name_t *keyname;
+ dns_tsig_keyring_t *ring = NULL;
+ dns_tsigkey_t *key = NULL;
+ isc_buffer_t *buf = NULL;
+ isc_buffer_t *querytsig = NULL;
+ isc_buffer_t *tsigin = NULL;
+ isc_buffer_t *tsigout = NULL;
+ isc_result_t result;
+ unsigned char secret[16] = { 0 };
+ dst_context_t *tsigctx = NULL;
+ dst_context_t *outctx = NULL;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(stderr, true);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ /* isc_log_setdebuglevel(lctx, 99); */
+
+ keyname = dns_fixedname_initname(&fkeyname);
+ result = dns_name_fromstring(keyname, "test", 0, NULL);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_tsigkeyring_create(mctx, &ring);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_tsigkey_create(keyname, dns_tsig_hmacsha256_name,
+ secret, sizeof(secret), false,
+ NULL, 0, 0, mctx, ring, &key);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ ATF_REQUIRE(key != NULL);
+
+ /*
+ * Create request.
+ */
+ result = isc_buffer_allocate(mctx, &buf, 65535);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ render(buf, 0, key, &tsigout, &querytsig, NULL);
+ isc_buffer_free(&buf);
+
+ /*
+ * Create response message 1.
+ */
+ result = isc_buffer_allocate(mctx, &buf, 65535);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ render(buf, DNS_MESSAGEFLAG_QR, key, &querytsig, &tsigout, NULL);
+
+ /*
+ * Process response message 1.
+ */
+ result = dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &msg);
+ ATF_REQUIRE_EQ_MSG(result, ISC_R_SUCCESS, "dns_message_create: %s",
+ dns_result_totext(result));
+ ATF_REQUIRE(msg != NULL);
+
+ result = dns_message_settsigkey(msg, key);
+ ATF_REQUIRE_EQ_MSG(result, ISC_R_SUCCESS, "dns_message_settsigkey: %s",
+ dns_result_totext(result));
+
+ result = dns_message_parse(msg, buf, 0);
+ ATF_REQUIRE_EQ_MSG(result, ISC_R_SUCCESS, "dns_message_parse: %s",
+ dns_result_totext(result));
+
+ printmessage(msg);
+
+ result = dns_message_setquerytsig(msg, querytsig);
+ ATF_REQUIRE_EQ_MSG(result, ISC_R_SUCCESS,
+ "dns_message_setquerytsig: %s",
+ dns_result_totext(result));
+
+ result = dns_tsig_verify(buf, msg, NULL, NULL);
+ ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS,
+ "dns_tsig_verify: %s",
+ dns_result_totext(result));
+ ATF_CHECK_EQ(msg->verified_sig, 1);
+ ATF_CHECK_EQ(msg->tsigstatus, dns_rcode_noerror);
+
+ /*
+ * Check that we have a TSIG in the first message.
+ */
+ ATF_REQUIRE(dns_message_gettsig(msg, &tsigowner) != NULL);
+
+ result = dns_message_getquerytsig(msg, mctx, &tsigin);
+ ATF_REQUIRE_EQ_MSG(result, ISC_R_SUCCESS,
+ "dns_message_getquerytsig: %s",
+ dns_result_totext(result));
+
+ tsigctx = msg->tsigctx;
+ msg->tsigctx = NULL;
+ isc_buffer_free(&buf);
+ dns_message_destroy(&msg);
+
+ result = dst_context_create3(key->key, mctx, DNS_LOGCATEGORY_DNSSEC,
+ false, &outctx);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ ATF_REQUIRE(outctx != NULL);
+
+ /*
+ * Start digesting.
+ */
+ result = add_mac(outctx, tsigout);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ /*
+ * Create response message 2.
+ */
+ result = isc_buffer_allocate(mctx, &buf, 65535);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ render(buf, DNS_MESSAGEFLAG_QR, key, &tsigout, &tsigout, outctx);
+
+ /*
+ * Process response message 2.
+ */
+ result = dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &msg);
+ ATF_REQUIRE_EQ_MSG(result, ISC_R_SUCCESS, "dns_message_create: %s",
+ dns_result_totext(result));
+ ATF_REQUIRE(msg != NULL);
+
+ msg->tcp_continuation = 1;
+ msg->tsigctx = tsigctx;
+ tsigctx = NULL;
+
+ result = dns_message_settsigkey(msg, key);
+ ATF_REQUIRE_EQ_MSG(result, ISC_R_SUCCESS, "dns_message_settsigkey: %s",
+ dns_result_totext(result));
+
+ result = dns_message_parse(msg, buf, 0);
+ ATF_REQUIRE_EQ_MSG(result, ISC_R_SUCCESS, "dns_message_parse: %s",
+ dns_result_totext(result));
+
+ printmessage(msg);
+
+ result = dns_message_setquerytsig(msg, tsigin);
+ ATF_REQUIRE_EQ_MSG(result, ISC_R_SUCCESS,
+ "dns_message_setquerytsig: %s",
+ dns_result_totext(result));
+
+ result = dns_tsig_verify(buf, msg, NULL, NULL);
+ ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS,
+ "dns_tsig_verify: %s",
+ dns_result_totext(result));
+ ATF_CHECK_EQ(msg->verified_sig, 0);
+ ATF_CHECK_EQ(msg->tsigstatus, dns_rcode_noerror);
+
+ /*
+ * Check that we don't have a TSIG in the second message.
+ */
+ tsigowner = NULL;
+ ATF_REQUIRE(dns_message_gettsig(msg, &tsigowner) == NULL);
+
+ tsigctx = msg->tsigctx;
+ msg->tsigctx = NULL;
+ isc_buffer_free(&buf);
+ dns_message_destroy(&msg);
+
+ /*
+ * Create response message 3.
+ */
+ result = isc_buffer_allocate(mctx, &buf, 65535);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ render(buf, DNS_MESSAGEFLAG_QR, key, &tsigout, &tsigout, outctx);
+
+ result = add_tsig(outctx, key, buf);
+ ATF_REQUIRE_EQ_MSG(result, ISC_R_SUCCESS, "add_tsig: %s",
+ dns_result_totext(result));
+
+ /*
+ * Process response message 3.
+ */
+ result = dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &msg);
+ ATF_REQUIRE_EQ_MSG(result, ISC_R_SUCCESS, "dns_message_create: %s",
+ dns_result_totext(result));
+ ATF_REQUIRE(msg != NULL);
+
+ msg->tcp_continuation = 1;
+ msg->tsigctx = tsigctx;
+ tsigctx = NULL;
+
+ result = dns_message_settsigkey(msg, key);
+ ATF_REQUIRE_EQ_MSG(result, ISC_R_SUCCESS, "dns_message_settsigkey: %s",
+ dns_result_totext(result));
+
+ result = dns_message_parse(msg, buf, 0);
+ ATF_REQUIRE_EQ_MSG(result, ISC_R_SUCCESS, "dns_message_parse: %s",
+ dns_result_totext(result));
+
+ printmessage(msg);
+
+ /*
+ * Check that we had a TSIG in the third message.
+ */
+ ATF_REQUIRE(dns_message_gettsig(msg, &tsigowner) != NULL);
+
+ result = dns_message_setquerytsig(msg, tsigin);
+ ATF_REQUIRE_EQ_MSG(result, ISC_R_SUCCESS,
+ "dns_message_setquerytsig: %s",
+ dns_result_totext(result));
+
+ result = dns_tsig_verify(buf, msg, NULL, NULL);
+ ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS,
+ "dns_tsig_verify: %s",
+ dns_result_totext(result));
+ ATF_CHECK_EQ(msg->verified_sig, 1);
+ ATF_CHECK_EQ(msg->tsigstatus, dns_rcode_noerror);
+
+ if (tsigin != NULL)
+ isc_buffer_free(&tsigin);
+
+ result = dns_message_getquerytsig(msg, mctx, &tsigin);
+ ATF_REQUIRE_EQ_MSG(result, ISC_R_SUCCESS,
+ "dns_message_getquerytsig: %s",
+ dns_result_totext(result));
+
+ isc_buffer_free(&buf);
+ dns_message_destroy(&msg);
+
+ if (outctx != NULL)
+ dst_context_destroy(&outctx);
+ if (querytsig != NULL)
+ isc_buffer_free(&querytsig);
+ if (tsigin != NULL)
+ isc_buffer_free(&tsigin);
+ if (tsigout != NULL)
+ isc_buffer_free(&tsigout);
+ dns_tsigkey_detach(&key);
+ if (ring != NULL)
+ dns_tsigkeyring_detach(&ring);
+ dns_test_end();
+}
+
+/*
+ * Main
+ */
+ATF_TP_ADD_TCS(tp) {
+ ATF_TP_ADD_TC(tp, tsig_tcp);
+ return (atf_no_error());
+}
diff --git a/lib/dns/tests/update_test.c b/lib/dns/tests/update_test.c
new file mode 100644
index 0000000..1585ed7
--- /dev/null
+++ b/lib/dns/tests/update_test.c
@@ -0,0 +1,343 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <atf-c.h>
+
+#include <inttypes.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <isc/serial.h>
+#include <isc/stdtime.h>
+
+#include <dns/update.h>
+
+#include "dnstest.h"
+
+static uint32_t mystdtime;
+
+static void set_mystdtime(int year, int month, int day) {
+ struct tm tm;
+
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_year = year - 1900;
+ tm.tm_mon = month - 1;
+ tm.tm_mday = day;
+ mystdtime = timegm(&tm) ;
+}
+
+void isc_stdtime_get(isc_stdtime_t *now) {
+ *now = mystdtime;
+}
+
+/*
+ * Individual unit tests
+ */
+
+ATF_TC(increment);
+ATF_TC_HEAD(increment, tc) {
+ atf_tc_set_md_var(tc, "descr", "simple increment by 1");
+}
+ATF_TC_BODY(increment, tc) {
+ uint32_t old = 50;
+ uint32_t serial;
+ isc_result_t result;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ serial = dns_update_soaserial(old, dns_updatemethod_increment);
+ ATF_REQUIRE_EQ(isc_serial_lt(old, serial), true);
+ ATF_CHECK_MSG(serial != 0, "serial (%d) should not equal 0", serial);
+ ATF_REQUIRE_EQ(serial, 51);
+ dns_test_end();
+}
+
+/* 0xfffffffff -> 1 */
+ATF_TC(increment_past_zero);
+ATF_TC_HEAD(increment_past_zero, tc) {
+ atf_tc_set_md_var(tc, "descr", "increment past zero, ffffffff -> 1");
+}
+ATF_TC_BODY(increment_past_zero, tc) {
+ uint32_t old = 0xffffffffu;
+ uint32_t serial;
+ isc_result_t result;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ serial = dns_update_soaserial(old, dns_updatemethod_increment);
+ ATF_REQUIRE_EQ(isc_serial_lt(old, serial), true);
+ ATF_CHECK(serial != 0);
+ ATF_REQUIRE_EQ(serial, 1u);
+ dns_test_end();
+}
+
+ATF_TC(past_to_unix);
+ATF_TC_HEAD(past_to_unix, tc) {
+ atf_tc_set_md_var(tc, "descr", "past to unixtime");
+}
+ATF_TC_BODY(past_to_unix, tc) {
+ uint32_t old;
+ uint32_t serial;
+ isc_result_t result;
+
+ UNUSED(tc);
+
+ set_mystdtime(2011, 6, 22);
+ old = mystdtime - 1;
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ serial = dns_update_soaserial(old, dns_updatemethod_unixtime);
+ ATF_REQUIRE_EQ(isc_serial_lt(old, serial), true);
+ ATF_CHECK(serial != 0);
+ ATF_REQUIRE_EQ(serial, mystdtime);
+ dns_test_end();
+}
+
+ATF_TC(now_to_unix);
+ATF_TC_HEAD(now_to_unix, tc) {
+ atf_tc_set_md_var(tc, "descr", "now to unixtime");
+}
+ATF_TC_BODY(now_to_unix, tc) {
+ uint32_t old;
+ uint32_t serial;
+ isc_result_t result;
+
+ UNUSED(tc);
+
+ set_mystdtime(2011, 6, 22);
+ old = mystdtime;
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ serial = dns_update_soaserial(old, dns_updatemethod_unixtime);
+ ATF_REQUIRE_EQ(isc_serial_lt(old, serial), true);
+ ATF_CHECK(serial != 0);
+ ATF_REQUIRE_EQ(serial, old + 1);
+ dns_test_end();
+}
+
+ATF_TC(future_to_unix);
+ATF_TC_HEAD(future_to_unix, tc) {
+ atf_tc_set_md_var(tc, "descr", "future to unixtime");
+}
+ATF_TC_BODY(future_to_unix, tc) {
+ uint32_t old;
+ uint32_t serial;
+ isc_result_t result;
+
+ UNUSED(tc);
+
+ set_mystdtime(2011, 6, 22);
+ old = mystdtime + 1;
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ serial = dns_update_soaserial(old, dns_updatemethod_unixtime);
+ ATF_REQUIRE_EQ(isc_serial_lt(old, serial), true);
+ ATF_CHECK(serial != 0);
+ ATF_REQUIRE_EQ(serial, old + 1);
+ dns_test_end();
+}
+
+ATF_TC(undefined_plus1_to_unix);
+ATF_TC_HEAD(undefined_plus1_to_unix, tc) {
+ atf_tc_set_md_var(tc, "descr", "undefined plus 1 to unixtime");
+}
+ATF_TC_BODY(undefined_plus1_to_unix, tc) {
+ uint32_t old;
+ uint32_t serial;
+ isc_result_t result;
+
+ UNUSED(tc);
+
+ set_mystdtime(2011, 6, 22);
+ old = mystdtime ^ 0x80000000u;
+ old += 1;
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ serial = dns_update_soaserial(old, dns_updatemethod_unixtime);
+ ATF_REQUIRE_EQ(isc_serial_lt(old, serial), true);
+ ATF_CHECK(serial != 0);
+ ATF_REQUIRE_EQ(serial, mystdtime);
+ dns_test_end();
+}
+
+ATF_TC(undefined_minus1_to_unix);
+ATF_TC_HEAD(undefined_minus1_to_unix, tc) {
+ atf_tc_set_md_var(tc, "descr", "undefined minus 1 to unixtime");
+}
+ATF_TC_BODY(undefined_minus1_to_unix, tc) {
+ uint32_t old;
+ uint32_t serial;
+ isc_result_t result;
+
+ UNUSED(tc);
+
+ set_mystdtime(2011, 6, 22);
+ old = mystdtime ^ 0x80000000u;
+ old -= 1;
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ serial = dns_update_soaserial(old, dns_updatemethod_unixtime);
+ ATF_REQUIRE_EQ(isc_serial_lt(old, serial), true);
+ ATF_CHECK(serial != 0);
+ ATF_REQUIRE_EQ(serial, old + 1);
+ dns_test_end();
+}
+
+ATF_TC(undefined_to_unix);
+ATF_TC_HEAD(undefined_to_unix, tc) {
+ atf_tc_set_md_var(tc, "descr", "undefined to unixtime");
+}
+ATF_TC_BODY(undefined_to_unix, tc) {
+ uint32_t old;
+ uint32_t serial;
+ isc_result_t result;
+
+ UNUSED(tc);
+
+ set_mystdtime(2011, 6, 22);
+ old = mystdtime ^ 0x80000000u;
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ serial = dns_update_soaserial(old, dns_updatemethod_unixtime);
+ ATF_REQUIRE_EQ(isc_serial_lt(old, serial), true);
+ ATF_CHECK(serial != 0);
+ ATF_REQUIRE_EQ(serial, old + 1);
+ dns_test_end();
+}
+
+ATF_TC(unixtime_zero);
+ATF_TC_HEAD(unixtime_zero, tc) {
+ atf_tc_set_md_var(tc, "descr", "handle unixtime being zero");
+}
+ATF_TC_BODY(unixtime_zero, tc) {
+ uint32_t old;
+ uint32_t serial;
+ isc_result_t result;
+
+ UNUSED(tc);
+
+ mystdtime = 0;
+ old = 0xfffffff0;
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ serial = dns_update_soaserial(old, dns_updatemethod_unixtime);
+ ATF_REQUIRE_EQ(isc_serial_lt(old, serial), true);
+ ATF_CHECK(serial != 0);
+ ATF_REQUIRE_EQ(serial, old + 1);
+ dns_test_end();
+}
+
+ATF_TC(past_to_date);
+ATF_TC_HEAD(past_to_date, tc) {
+ atf_tc_set_md_var(tc, "descr", "past to date");
+}
+ATF_TC_BODY(past_to_date, tc) {
+ uint32_t old, serial;
+ isc_result_t result;
+
+ UNUSED(tc);
+
+ set_mystdtime(2014, 3, 31);
+ old = dns_update_soaserial(0, dns_updatemethod_date);
+ set_mystdtime(2014, 4, 1);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ serial = dns_update_soaserial(old, dns_updatemethod_date);
+ ATF_REQUIRE_EQ(isc_serial_lt(old, serial), true);
+ ATF_CHECK(serial != 0);
+ ATF_REQUIRE_EQ(serial, 2014040100);
+ dns_test_end();
+}
+
+ATF_TC(now_to_date);
+ATF_TC_HEAD(now_to_date, tc) {
+ atf_tc_set_md_var(tc, "descr", "now to date");
+}
+ATF_TC_BODY(now_to_date, tc) {
+ uint32_t old;
+ uint32_t serial;
+ isc_result_t result;
+
+ UNUSED(tc);
+
+ set_mystdtime(2014, 4, 1);
+ old = dns_update_soaserial(0, dns_updatemethod_date);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ serial = dns_update_soaserial(old, dns_updatemethod_date);
+ ATF_REQUIRE_EQ(isc_serial_lt(old, serial), true);
+ ATF_CHECK(serial != 0);
+ ATF_REQUIRE_EQ(serial, 2014040101);
+ dns_test_end();
+}
+
+ATF_TC(future_to_date);
+ATF_TC_HEAD(future_to_date, tc) {
+ atf_tc_set_md_var(tc, "descr", "future to date");
+}
+ATF_TC_BODY(future_to_date, tc) {
+ uint32_t old;
+ uint32_t serial;
+ isc_result_t result;
+
+ UNUSED(tc);
+
+ set_mystdtime(2014, 4, 1);
+ old = dns_update_soaserial(0, dns_updatemethod_date);
+ set_mystdtime(2014, 3, 31);
+
+ result = dns_test_begin(NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ serial = dns_update_soaserial(old, dns_updatemethod_date);
+ ATF_REQUIRE_EQ(isc_serial_lt(old, serial), true);
+ ATF_CHECK(serial != 0);
+ ATF_REQUIRE_EQ(serial, 2014040101);
+ dns_test_end();
+}
+
+/*
+ * Main
+ */
+ATF_TP_ADD_TCS(tp) {
+ ATF_TP_ADD_TC(tp, increment);
+ ATF_TP_ADD_TC(tp, increment_past_zero);
+ ATF_TP_ADD_TC(tp, past_to_unix);
+ ATF_TP_ADD_TC(tp, now_to_unix);
+ ATF_TP_ADD_TC(tp, future_to_unix);
+ ATF_TP_ADD_TC(tp, undefined_to_unix);
+ ATF_TP_ADD_TC(tp, undefined_plus1_to_unix);
+ ATF_TP_ADD_TC(tp, undefined_minus1_to_unix);
+ ATF_TP_ADD_TC(tp, unixtime_zero);
+ ATF_TP_ADD_TC(tp, past_to_date);
+ ATF_TP_ADD_TC(tp, now_to_date);
+ ATF_TP_ADD_TC(tp, future_to_date);
+
+ return (atf_no_error());
+}
+
diff --git a/lib/dns/tests/zonemgr_test.c b/lib/dns/tests/zonemgr_test.c
new file mode 100644
index 0000000..03079c6
--- /dev/null
+++ b/lib/dns/tests/zonemgr_test.c
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <atf-c.h>
+
+#include <unistd.h>
+
+#include <isc/buffer.h>
+#include <isc/task.h>
+#include <isc/timer.h>
+
+#include <dns/name.h>
+#include <dns/view.h>
+#include <dns/zone.h>
+
+#include "dnstest.h"
+
+/*
+ * Individual unit tests
+ */
+ATF_TC(zonemgr_create);
+ATF_TC_HEAD(zonemgr_create, tc) {
+ atf_tc_set_md_var(tc, "descr", "create zone manager");
+}
+ATF_TC_BODY(zonemgr_create, tc) {
+ dns_zonemgr_t *myzonemgr = NULL;
+ isc_result_t result;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, true);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_zonemgr_create(mctx, taskmgr, timermgr, socketmgr,
+ &myzonemgr);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ dns_zonemgr_shutdown(myzonemgr);
+ dns_zonemgr_detach(&myzonemgr);
+ ATF_REQUIRE_EQ(myzonemgr, NULL);
+
+ dns_test_end();
+}
+
+
+ATF_TC(zonemgr_managezone);
+ATF_TC_HEAD(zonemgr_managezone, tc) {
+ atf_tc_set_md_var(tc, "descr", "manage and release a zone");
+}
+ATF_TC_BODY(zonemgr_managezone, tc) {
+ dns_zonemgr_t *myzonemgr = NULL;
+ dns_zone_t *zone = NULL;
+ isc_result_t result;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, true);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_zonemgr_create(mctx, taskmgr, timermgr, socketmgr,
+ &myzonemgr);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_test_makezone("foo", &zone, NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ /* This should not succeed until the dns_zonemgr_setsize() is run */
+ result = dns_zonemgr_managezone(myzonemgr, zone);
+ ATF_REQUIRE_EQ(result, ISC_R_FAILURE);
+
+ ATF_REQUIRE_EQ(dns_zonemgr_getcount(myzonemgr, DNS_ZONESTATE_ANY), 0);
+
+ result = dns_zonemgr_setsize(myzonemgr, 1);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ /* Now it should succeed */
+ result = dns_zonemgr_managezone(myzonemgr, zone);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ ATF_REQUIRE_EQ(dns_zonemgr_getcount(myzonemgr, DNS_ZONESTATE_ANY), 1);
+
+ dns_zonemgr_releasezone(myzonemgr, zone);
+ dns_zone_detach(&zone);
+
+ ATF_REQUIRE_EQ(dns_zonemgr_getcount(myzonemgr, DNS_ZONESTATE_ANY), 0);
+
+ dns_zonemgr_shutdown(myzonemgr);
+ dns_zonemgr_detach(&myzonemgr);
+ ATF_REQUIRE_EQ(myzonemgr, NULL);
+
+ dns_test_end();
+}
+
+ATF_TC(zonemgr_createzone);
+ATF_TC_HEAD(zonemgr_createzone, tc) {
+ atf_tc_set_md_var(tc, "descr", "create and release a zone");
+}
+ATF_TC_BODY(zonemgr_createzone, tc) {
+ dns_zonemgr_t *myzonemgr = NULL;
+ dns_zone_t *zone = NULL;
+ isc_result_t result;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, true);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_zonemgr_create(mctx, taskmgr, timermgr, socketmgr,
+ &myzonemgr);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ /* This should not succeed until the dns_zonemgr_setsize() is run */
+ result = dns_zonemgr_createzone(myzonemgr, &zone);
+ ATF_REQUIRE_EQ(result, ISC_R_FAILURE);
+
+ result = dns_zonemgr_setsize(myzonemgr, 1);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ /* Now it should succeed */
+ result = dns_zonemgr_createzone(myzonemgr, &zone);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ ATF_CHECK(zone != NULL);
+
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+
+ dns_zonemgr_shutdown(myzonemgr);
+ dns_zonemgr_detach(&myzonemgr);
+ ATF_REQUIRE_EQ(myzonemgr, NULL);
+
+ dns_test_end();
+}
+
+ATF_TC(zonemgr_unreachable);
+ATF_TC_HEAD(zonemgr_unreachable, tc) {
+ atf_tc_set_md_var(tc, "descr", "manage and release a zone");
+}
+ATF_TC_BODY(zonemgr_unreachable, tc) {
+ dns_zonemgr_t *myzonemgr = NULL;
+ dns_zone_t *zone = NULL;
+ isc_sockaddr_t addr1, addr2;
+ struct in_addr in;
+ isc_result_t result;
+ isc_time_t now;
+
+ UNUSED(tc);
+
+ TIME_NOW(&now);
+
+ result = dns_test_begin(NULL, true);
+
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_zonemgr_create(mctx, taskmgr, timermgr, socketmgr,
+ &myzonemgr);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_test_makezone("foo", &zone, NULL, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_zonemgr_setsize(myzonemgr, 1);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_zonemgr_managezone(myzonemgr, zone);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ in.s_addr = inet_addr("10.53.0.1");
+ isc_sockaddr_fromin(&addr1, &in, 2112);
+ in.s_addr = inet_addr("10.53.0.2");
+ isc_sockaddr_fromin(&addr2, &in, 5150);
+ ATF_CHECK(! dns_zonemgr_unreachable(myzonemgr, &addr1, &addr2, &now));
+ /*
+ * We require multiple unreachableadd calls to mark a server as
+ * unreachable.
+ */
+ dns_zonemgr_unreachableadd(myzonemgr, &addr1, &addr2, &now);
+ ATF_CHECK(! dns_zonemgr_unreachable(myzonemgr, &addr1, &addr2, &now));
+ dns_zonemgr_unreachableadd(myzonemgr, &addr1, &addr2, &now);
+ ATF_CHECK(dns_zonemgr_unreachable(myzonemgr, &addr1, &addr2, &now));
+
+ in.s_addr = inet_addr("10.53.0.3");
+ isc_sockaddr_fromin(&addr2, &in, 5150);
+ ATF_CHECK(! dns_zonemgr_unreachable(myzonemgr, &addr1, &addr2, &now));
+ /*
+ * We require multiple unreachableadd calls to mark a server as
+ * unreachable.
+ */
+ dns_zonemgr_unreachableadd(myzonemgr, &addr1, &addr2, &now);
+ dns_zonemgr_unreachableadd(myzonemgr, &addr1, &addr2, &now);
+ ATF_CHECK(dns_zonemgr_unreachable(myzonemgr, &addr1, &addr2, &now));
+
+ dns_zonemgr_unreachabledel(myzonemgr, &addr1, &addr2);
+ ATF_CHECK(! dns_zonemgr_unreachable(myzonemgr, &addr1, &addr2, &now));
+
+ in.s_addr = inet_addr("10.53.0.2");
+ isc_sockaddr_fromin(&addr2, &in, 5150);
+ ATF_CHECK(dns_zonemgr_unreachable(myzonemgr, &addr1, &addr2, &now));
+ dns_zonemgr_unreachabledel(myzonemgr, &addr1, &addr2);
+ ATF_CHECK(! dns_zonemgr_unreachable(myzonemgr, &addr1, &addr2, &now));
+
+ dns_zonemgr_releasezone(myzonemgr, zone);
+ dns_zone_detach(&zone);
+ dns_zonemgr_shutdown(myzonemgr);
+ dns_zonemgr_detach(&myzonemgr);
+ ATF_REQUIRE_EQ(myzonemgr, NULL);
+
+ dns_test_end();
+}
+
+
+/*
+ * Main
+ */
+ATF_TP_ADD_TCS(tp) {
+ ATF_TP_ADD_TC(tp, zonemgr_create);
+ ATF_TP_ADD_TC(tp, zonemgr_managezone);
+ ATF_TP_ADD_TC(tp, zonemgr_createzone);
+ ATF_TP_ADD_TC(tp, zonemgr_unreachable);
+ return (atf_no_error());
+}
+
+/*
+ * XXX:
+ * dns_zonemgr API calls that are not yet part of this unit test:
+ *
+ * - dns_zonemgr_attach
+ * - dns_zonemgr_forcemaint
+ * - dns_zonemgr_resumexfrs
+ * - dns_zonemgr_shutdown
+ * - dns_zonemgr_setsize
+ * - dns_zonemgr_settransfersin
+ * - dns_zonemgr_getttransfersin
+ * - dns_zonemgr_settransfersperns
+ * - dns_zonemgr_getttransfersperns
+ * - dns_zonemgr_setiolimit
+ * - dns_zonemgr_getiolimit
+ * - dns_zonemgr_dbdestroyed
+ * - dns_zonemgr_setserialqueryrate
+ * - dns_zonemgr_getserialqueryrate
+ */
diff --git a/lib/dns/tests/zt_test.c b/lib/dns/tests/zt_test.c
new file mode 100644
index 0000000..d2756c2
--- /dev/null
+++ b/lib/dns/tests/zt_test.c
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <atf-c.h>
+
+#include <stdbool.h>
+#include <unistd.h>
+
+#include <isc/app.h>
+#include <isc/buffer.h>
+#include <isc/print.h>
+#include <isc/task.h>
+#include <isc/timer.h>
+
+#include <dns/db.h>
+#include <dns/name.h>
+#include <dns/view.h>
+#include <dns/zone.h>
+#include <dns/zt.h>
+
+#include "dnstest.h"
+
+struct args {
+ void *arg1;
+ void *arg2;
+ bool arg3;
+};
+
+/*
+ * Helper functions
+ */
+static isc_result_t
+count_zone(dns_zone_t *zone, void *uap) {
+ int *nzones = (int *)uap;
+
+ UNUSED(zone);
+
+ *nzones += 1;
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+load_done(dns_zt_t *zt, dns_zone_t *zone, isc_task_t *task) {
+ /* We treat zt as a pointer to a boolean for testing purposes */
+ bool *done = (bool *) zt;
+
+ UNUSED(zone);
+ UNUSED(task);
+
+ *done = true;
+ isc_app_shutdown();
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+all_done(void *arg) {
+ bool *done = (bool *) arg;
+
+ *done = true;
+ isc_app_shutdown();
+ return (ISC_R_SUCCESS);
+}
+
+static void
+start_zt_asyncload(isc_task_t *task, isc_event_t *event) {
+ struct args *args = (struct args *)(event->ev_arg);
+
+ UNUSED(task);
+
+ dns_zt_asyncload2(args->arg1, all_done, args->arg2, false);
+
+ isc_event_free(&event);
+}
+
+static void
+start_zone_asyncload(isc_task_t *task, isc_event_t *event) {
+ struct args *args = (struct args *)(event->ev_arg);
+
+ UNUSED(task);
+
+ dns_zone_asyncload2(args->arg1, load_done, args->arg2, args->arg3);
+ isc_event_free(&event);
+}
+
+/*
+ * Individual unit tests
+ */
+ATF_TC(apply);
+ATF_TC_HEAD(apply, tc) {
+ atf_tc_set_md_var(tc, "descr", "apply a function to a zone table");
+}
+ATF_TC_BODY(apply, tc) {
+ isc_result_t result;
+ dns_zone_t *zone = NULL;
+ dns_view_t *view = NULL;
+ int nzones = 0;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, true);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_test_makezone("foo", &zone, NULL, true);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ view = dns_zone_getview(zone);
+ ATF_REQUIRE(view->zonetable != NULL);
+
+ ATF_CHECK_EQ(0, nzones);
+ result = dns_zt_apply(view->zonetable, false, count_zone, &nzones);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ ATF_CHECK_EQ(1, nzones);
+
+ /* These steps are necessary so the zone can be detached properly */
+ result = dns_test_setupzonemgr();
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ result = dns_test_managezone(zone);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ dns_test_releasezone(zone);
+ dns_test_closezonemgr();
+
+ /* The view was left attached in dns_test_makezone() */
+ dns_view_detach(&view);
+ dns_zone_detach(&zone);
+
+ dns_test_end();
+}
+
+ATF_TC(asyncload_zone);
+ATF_TC_HEAD(asyncload_zone, tc) {
+ atf_tc_set_md_var(tc, "descr", "asynchronous zone load");
+}
+ATF_TC_BODY(asyncload_zone, tc) {
+ isc_result_t result;
+ int n;
+ dns_zone_t *zone = NULL;
+ dns_view_t *view = NULL;
+ dns_db_t *db = NULL;
+ FILE* zonefile, *origfile;
+ char buf[4096];
+ bool done = false;
+ int i = 0;
+ struct args args;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, true);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_test_makezone("foo", &zone, NULL, true);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_test_setupzonemgr();
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ result = dns_test_managezone(zone);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ view = dns_zone_getview(zone);
+ ATF_REQUIRE(view->zonetable != NULL);
+
+ ATF_CHECK(!dns__zone_loadpending(zone));
+ ATF_CHECK(!done);
+ zonefile = fopen("./zone.data", "wb");
+ ATF_CHECK(zonefile != NULL);
+ origfile = fopen("./testdata/zt/zone1.db", "r+b");
+ ATF_CHECK(origfile != NULL);
+ n = fread(buf, 1, 4096, origfile);
+ fclose(origfile);
+ fwrite(buf, 1, n, zonefile);
+ fflush(zonefile);
+
+ dns_zone_setfile(zone, "./zone.data");
+
+ args.arg1 = zone;
+ args.arg2 = &done;
+ args.arg3 = false;
+ isc_app_onrun(mctx, maintask, start_zone_asyncload, &args);
+
+ isc_app_run();
+ while (dns__zone_loadpending(zone) && i++ < 5000)
+ dns_test_nap(1000);
+ ATF_CHECK(done);
+ /* The zone should now be loaded; test it */
+ result = dns_zone_getdb(zone, &db);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ dns_db_detach(&db);
+ /*
+ * Add something to zone file, reload zone with newonly - it should
+ * not be reloaded.
+ */
+ fprintf(zonefile, "\nb in b 1.2.3.4\n");
+ fflush(zonefile);
+ fclose(zonefile);
+
+ args.arg1 = zone;
+ args.arg2 = &done;
+ args.arg3 = false;
+ isc_app_onrun(mctx, maintask, start_zone_asyncload, &args);
+
+ isc_app_run();
+
+ while (dns__zone_loadpending(zone) && i++ < 5000)
+ dns_test_nap(1000);
+ ATF_CHECK(done);
+ /* The zone should now be loaded; test it */
+ result = dns_zone_getdb(zone, &db);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ dns_db_detach(&db);
+
+ /* Now reload it without newonly - it should be reloaded */
+ args.arg1 = zone;
+ args.arg2 = &done;
+ args.arg3 = false;
+ isc_app_onrun(mctx, maintask, start_zone_asyncload, &args);
+
+ isc_app_run();
+
+ while (dns__zone_loadpending(zone) && i++ < 5000)
+ dns_test_nap(1000);
+ ATF_CHECK(done);
+ /* The zone should now be loaded; test it */
+ result = dns_zone_getdb(zone, &db);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+ ATF_CHECK(db != NULL);
+ if (db != NULL)
+ dns_db_detach(&db);
+
+ dns_test_releasezone(zone);
+ dns_test_closezonemgr();
+
+ dns_zone_detach(&zone);
+ dns_view_detach(&view);
+
+ dns_test_end();
+}
+
+ATF_TC(asyncload_zt);
+ATF_TC_HEAD(asyncload_zt, tc) {
+ atf_tc_set_md_var(tc, "descr", "asynchronous zone table load");
+}
+ATF_TC_BODY(asyncload_zt, tc) {
+ isc_result_t result;
+ dns_zone_t *zone1 = NULL, *zone2 = NULL, *zone3 = NULL;
+ dns_view_t *view;
+ dns_zt_t *zt;
+ dns_db_t *db = NULL;
+ bool done = false;
+ int i = 0;
+ struct args args;
+
+ UNUSED(tc);
+
+ result = dns_test_begin(NULL, true);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = dns_test_makezone("foo", &zone1, NULL, true);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ dns_zone_setfile(zone1, "testdata/zt/zone1.db");
+ view = dns_zone_getview(zone1);
+
+ result = dns_test_makezone("bar", &zone2, view, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ dns_zone_setfile(zone2, "testdata/zt/zone1.db");
+
+ /* This one will fail to load */
+ result = dns_test_makezone("fake", &zone3, view, false);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ dns_zone_setfile(zone3, "testdata/zt/nonexistent.db");
+
+ zt = view->zonetable;
+ ATF_REQUIRE(zt != NULL);
+
+ result = dns_test_setupzonemgr();
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ result = dns_test_managezone(zone1);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ result = dns_test_managezone(zone2);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ result = dns_test_managezone(zone3);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ ATF_CHECK(!dns__zone_loadpending(zone1));
+ ATF_CHECK(!dns__zone_loadpending(zone2));
+ ATF_CHECK(!done);
+
+ args.arg1 = zt;
+ args.arg2 = &done;
+ isc_app_onrun(mctx, maintask, start_zt_asyncload, &args);
+
+ isc_app_run();
+ while (!done && i++ < 5000)
+ dns_test_nap(1000);
+ ATF_CHECK(done);
+
+ /* Both zones should now be loaded; test them */
+ result = dns_zone_getdb(zone1, &db);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ ATF_CHECK(db != NULL);
+ if (db != NULL)
+ dns_db_detach(&db);
+
+ result = dns_zone_getdb(zone2, &db);
+ ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+ ATF_CHECK(db != NULL);
+ if (db != NULL)
+ dns_db_detach(&db);
+
+ dns_test_releasezone(zone3);
+ dns_test_releasezone(zone2);
+ dns_test_releasezone(zone1);
+ dns_test_closezonemgr();
+
+ dns_zone_detach(&zone1);
+ dns_zone_detach(&zone2);
+ dns_zone_detach(&zone3);
+ dns_view_detach(&view);
+
+ dns_test_end();
+}
+
+/*
+ * Main
+ */
+ATF_TP_ADD_TCS(tp) {
+ ATF_TP_ADD_TC(tp, apply);
+ ATF_TP_ADD_TC(tp, asyncload_zone);
+ ATF_TP_ADD_TC(tp, asyncload_zt);
+ return (atf_no_error());
+}
diff --git a/lib/dns/time.c b/lib/dns/time.c
new file mode 100644
index 0000000..dedd518
--- /dev/null
+++ b/lib/dns/time.c
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <inttypes.h>
+#include <isc/string.h> /* Required for HP/UX (and others?) */
+#include <time.h>
+#include <ctype.h>
+
+#include <isc/print.h>
+#include <isc/region.h>
+#include <isc/serial.h>
+#include <isc/stdtime.h>
+#include <isc/util.h>
+
+#include <dns/result.h>
+#include <dns/time.h>
+
+static const int days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+isc_result_t
+dns_time64_totext(int64_t t, isc_buffer_t *target) {
+ struct tm tm;
+ char buf[sizeof("!!!!!!YYYY!!!!!!!!MM!!!!!!!!DD!!!!!!!!HH!!!!!!!!MM!!!!!!!!SS")];
+ int secs;
+ unsigned int l;
+ isc_region_t region;
+
+/*
+ * Warning. Do NOT use arguments with side effects with these macros.
+ */
+#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 (t < 0) {
+ if (tm.tm_year == 0)
+ return (ISC_R_RANGE);
+ tm.tm_year--;
+ secs = year_secs(tm.tm_year + 1900);
+ t += secs;
+ }
+ 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);
+
+ memmove(region.base, buf, l);
+ isc_buffer_add(target, l);
+ return (ISC_R_SUCCESS);
+}
+
+int64_t
+dns_time64_from32(uint32_t value) {
+ isc_stdtime_t now;
+ int64_t start;
+ 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 = (int64_t) now;
+ if (isc_serial_gt(value, now))
+ t = start + (value - now);
+ else
+ t = start - (now - value);
+
+ return (t);
+}
+
+isc_result_t
+dns_time32_totext(uint32_t value, isc_buffer_t *target) {
+ return (dns_time64_totext(dns_time64_from32(value), target));
+}
+
+isc_result_t
+dns_time64_fromtext(const char *source, int64_t *target) {
+ int year, month, day, hour, minute, second;
+ 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);
+ /*
+ * Confirm the source only consists digits. sscanf() allows some
+ * minor exceptions.
+ */
+ for (i = 0; i < 14; i++) {
+ if (!isdigit((unsigned char)source[i]))
+ 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(0, 9999, year);
+ RANGE(1, 12, month);
+ RANGE(1, days[month - 1] +
+ ((month == 2 && is_leap(year)) ? 1 : 0), day);
+#ifdef __COVERITY__
+ /*
+ * Use a simplified range to silence Coverity warning (in
+ * arithmetic with day below).
+ */
+ RANGE(1, 31, day);
+#endif /* __COVERITY__ */
+
+ RANGE(0, 23, hour);
+ RANGE(0, 59, minute);
+ RANGE(0, 60, second); /* 60 == leap second. */
+
+ /*
+ * Calculate seconds from epoch.
+ * Note: this uses a idealized calendar.
+ */
+ 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;
+ if (year < 1970) {
+ for (i = 1969; i >= year; i--) {
+ secs = (is_leap(i) ? 366 : 365) * 86400;
+ value -= secs;
+ }
+ } else {
+ 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, uint32_t *target) {
+ int64_t value64;
+ isc_result_t result;
+ result = dns_time64_fromtext(source, &value64);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ *target = (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..8c5a328
--- /dev/null
+++ b/lib/dns/timer.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdbool.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, bool 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..5b4ffd9
--- /dev/null
+++ b/lib/dns/tkey.c
@@ -0,0 +1,1526 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/buffer.h>
+#include <isc/entropy.h>
+#include <isc/md5.h>
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <pk11/site.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>
+
+#include "dst_internal.h"
+
+#define TEMP_BUFFER_SZ 8192
+#define TKEY_RANDOM_AMOUNT 16
+
+#ifdef PKCS11CRYPTO
+#include <pk11/pk11.h>
+#endif
+
+#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
+dumpmessage(dns_message_t *msg) {
+ isc_buffer_t outbuf;
+ unsigned char *output;
+ int len = TEMP_BUFFER_SZ;
+ isc_result_t result;
+
+ for (;;) {
+ output = isc_mem_get(msg->mctx, len);
+ if (output == NULL)
+ return;
+
+ isc_buffer_init(&outbuf, output, len);
+ result = dns_message_totext(msg, &dns_master_style_debug,
+ 0, &outbuf);
+ if (result == ISC_R_NOSPACE) {
+ isc_mem_put(msg->mctx, output, len);
+ len *= 2;
+ continue;
+ }
+
+ if (result == ISC_R_SUCCESS)
+ tkey_log("%.*s",
+ (int)isc_buffer_usedlength(&outbuf),
+ (char *)isc_buffer_base(&outbuf));
+ else
+ tkey_log("Warning: dns_message_totext: %s",
+ dns_result_totext(result));
+ break;
+ }
+
+ if (output != NULL)
+ isc_mem_put(msg->mctx, output, len);
+}
+
+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;
+ tctx->gssapi_keytab = 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->gssapi_keytab != NULL) {
+ isc_mem_free(mctx, tctx->gssapi_keytab);
+ }
+ 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,
+ 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);
+ memmove(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->ttl = ttl;
+ ISC_LIST_APPEND(newlist->rdata, newrdata, link);
+
+ RETERR(dns_message_gettemprdataset(msg, &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)) {
+ INSIST(newlist != NULL);
+ 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)
+{
+#ifndef PK11_MD5_DISABLE
+ 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)) {
+ memmove(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 {
+ memmove(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);
+#else
+ UNUSED(shared);
+ UNUSED(queryrandomness);
+ UNUSED(serverrandomness);
+ UNUSED(secret);
+
+ return (ISC_R_NOTIMPLEMENTED);
+#endif
+}
+
+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;
+ bool found_key = false, found_incompatible = 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);
+ }
+
+#ifndef PK11_MD5_DISABLE
+ 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);
+ }
+#else
+ tkey_log("process_dhtkey: MD5 was disabled");
+ tkeyout->error = dns_tsigerror_badalg;
+ return (ISC_R_SUCCESS);
+#endif
+
+ /*
+ * 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;
+ }
+#ifndef PK11_DH_DISABLE
+ if (dst_key_alg(pubkey) == DNS_KEYALG_DH) {
+ if (dst_key_paramcompare(pubkey, tctx->dhkey))
+ {
+ found_key = true;
+ ttl = keyset->ttl;
+ break;
+ } else
+ found_incompatible = true;
+ }
+#endif
+ 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 = dst__entropy_getdata(randomdata, TKEY_RANDOM_AMOUNT,
+ false);
+ 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),
+ 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_name_t *name, dns_rdata_tkey_t *tkeyin,
+ dns_tkeyctx_t *tctx, dns_rdata_tkey_t *tkeyout,
+ dns_tsig_keyring_t *ring)
+{
+ isc_result_t result = ISC_R_SUCCESS;
+ dst_key_t *dstkey = NULL;
+ dns_tsigkey_t *tsigkey = NULL;
+ dns_fixedname_t fixed;
+ dns_name_t *principal;
+ isc_stdtime_t now;
+ isc_region_t intoken;
+ isc_buffer_t *outtoken = NULL;
+ gss_ctx_id_t gss_ctx = NULL;
+
+ /*
+ * You have to define either a gss credential (principal) to
+ * accept with tkey-gssapi-credential, or you have to
+ * configure a specific keytab (with tkey-gssapi-keytab) in
+ * order to use gsstkey
+ */
+ if (tctx->gsscred == NULL && tctx->gssapi_keytab == NULL) {
+ tkey_log("process_gsstkey(): no tkey-gssapi-credential "
+ "or tkey-gssapi-keytab configured");
+ 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);
+
+ principal = dns_fixedname_initname(&fixed);
+
+ /*
+ * Note that tctx->gsscred may be NULL if tctx->gssapi_keytab is set
+ */
+ result = dst_gssapi_acceptctx(tctx->gsscred, tctx->gssapi_keytab,
+ &intoken, &outtoken, &gss_ctx,
+ principal, tctx->mctx);
+ if (result == DNS_R_INVALIDTKEY) {
+ if (tsigkey != NULL)
+ dns_tsigkey_detach(&tsigkey);
+ tkeyout->error = dns_tsigerror_badkey;
+ tkey_log("process_gsstkey(): dns_tsigerror_badkey"); /* XXXSRA */
+ return (ISC_R_SUCCESS);
+ }
+ if (result != DNS_R_CONTINUE && result != ISC_R_SUCCESS)
+ goto failure;
+ /*
+ * XXXDCL Section 4.1.3: Limit GSS_S_CONTINUE_NEEDED to 10 times.
+ */
+
+ isc_stdtime_get(&now);
+
+ if (dns_name_countlabels(principal) == 0U) {
+ if (tsigkey != NULL)
+ dns_tsigkey_detach(&tsigkey);
+ } else if (tsigkey == NULL) {
+#ifdef GSSAPI
+ OM_uint32 gret, minor, lifetime;
+#endif
+ uint32_t expire;
+
+ RETERR(dst_key_fromgssapi(name, gss_ctx, ring->mctx,
+ &dstkey, &intoken));
+ /*
+ * Limit keys to 1 hour or the context's lifetime whichever
+ * is smaller.
+ */
+ expire = now + 3600;
+#ifdef GSSAPI
+ gret = gss_context_time(&minor, gss_ctx, &lifetime);
+ if (gret == GSS_S_COMPLETE && now + lifetime < expire)
+ expire = now + lifetime;
+#endif
+ RETERR(dns_tsigkey_createfromkey(name, &tkeyin->algorithm,
+ dstkey, true, principal,
+ now, expire, ring->mctx, ring,
+ NULL));
+ dst_key_free(&dstkey);
+ tkeyout->inception = now;
+ tkeyout->expire = expire;
+ } else {
+ tkeyout->inception = tsigkey->inception;
+ tkeyout->expire = tsigkey->expire;
+ dns_tsigkey_detach(&tsigkey);
+ }
+
+ 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);
+ memmove(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;
+ memmove(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 (tsigkey != NULL)
+ dns_tsigkey_detach(&tsigkey);
+
+ 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_name_t *signer, dns_name_t *name,
+ dns_rdata_tkey_t *tkeyin, dns_rdata_tkey_t *tkeyout,
+ dns_tsig_keyring_t *ring)
+{
+ isc_result_t result;
+ dns_tsigkey_t *tsigkey = NULL;
+ dns_name_t *identity;
+
+ 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;
+ bool freetkeyin = 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.
+ */
+ name = NULL;
+ 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 = 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;
+ }
+
+ keyname = dns_fixedname_initname(&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, 0, 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(keyname, &tkeyin, tctx,
+ &tkeyout, ring));
+ break;
+ case DNS_TKEYMODE_DELETE:
+ tkeyout.error = dns_rcode_noerror;
+ RETERR(process_deletetkey(signer, keyname, &tkeyin,
+ &tkeyout, ring));
+ 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 = 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, 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, bool 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, *anamebuf = NULL, *qnamebuf = NULL;
+ isc_result_t result;
+ unsigned int len;
+
+ 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_makequestion(question, dns_rdataclass_any,
+ dns_rdatatype_tkey);
+
+ len = 16 + tkey->algorithm.length + tkey->keylen + tkey->otherlen;
+ RETERR(isc_buffer_allocate(msg->mctx, &dynbuf, len));
+ RETERR(isc_buffer_allocate(msg->mctx, &anamebuf, name->length));
+ RETERR(isc_buffer_allocate(msg->mctx, &qnamebuf, name->length));
+ 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;
+ ISC_LIST_APPEND(tkeylist->rdata, rdata, link);
+
+ RETERR(dns_message_gettemprdataset(msg, &tkeyset));
+ RETERR(dns_rdatalist_tordataset(tkeylist, tkeyset));
+
+ dns_name_init(qname, NULL);
+ RETERR(dns_name_copy(name, qname, qnamebuf));
+
+ dns_name_init(aname, NULL);
+ RETERR(dns_name_copy(name, aname, anamebuf));
+
+ ISC_LIST_APPEND(qname->list, question, link);
+ ISC_LIST_APPEND(aname->list, tkeyset, link);
+
+ dns_message_addname(msg, qname, DNS_SECTION_QUESTION);
+ dns_message_takebuffer(msg, &qnamebuf);
+
+ /*
+ * 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);
+ dns_message_takebuffer(msg, &anamebuf);
+
+ 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);
+ if (qnamebuf != NULL)
+ isc_buffer_free(&qnamebuf);
+ if (anamebuf != NULL)
+ isc_buffer_free(&anamebuf);
+ 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,
+ 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 = NULL;
+ 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, false));
+
+ 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));
+ 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_ADDITIONAL);
+ name = next;
+ }
+
+ 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, uint32_t lifetime,
+ gss_ctx_id_t *context, bool win2k,
+ isc_mem_t *mctx, char **err_message)
+{
+ dns_rdata_tkey_t tkey;
+ isc_result_t result;
+ isc_stdtime_t now;
+ isc_buffer_t token;
+ unsigned char array[TEMP_BUFFER_SZ];
+
+ UNUSED(intoken);
+
+ REQUIRE(msg != NULL);
+ REQUIRE(name != NULL);
+ REQUIRE(gname != NULL);
+ REQUIRE(context != NULL);
+ REQUIRE(mctx != NULL);
+
+ isc_buffer_init(&token, array, sizeof(array));
+ result = dst_gssapi_initctx(gname, NULL, &token, context,
+ mctx, err_message);
+ 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;
+
+ return (buildquery(msg, name, &tkey, win2k));
+}
+
+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, 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;
+ bool freertkey = 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 = 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 = NULL;
+ r2.length = 0;
+ }
+ RETERR(compute_secret(shared, &r2, &r, &secret));
+
+ isc_buffer_usedregion(&secret, &r);
+ result = dns_tsigkey_create(tkeyname, &rtkey.algorithm,
+ r.base, r.length, 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, char **err_message)
+{
+ 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[TEMP_BUFFER_SZ];
+
+ REQUIRE(outtoken != NULL);
+ REQUIRE(qmsg != NULL);
+ REQUIRE(rmsg != NULL);
+ REQUIRE(gname != NULL);
+ REQUIRE(ring != 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);
+ dumpmessage(qmsg);
+ 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,
+ ring->mctx, err_message));
+
+ RETERR(dst_key_fromgssapi(dns_rootname, *context, rmsg->mctx,
+ &dstkey, NULL));
+
+ RETERR(dns_tsigkey_createfromkey(tkeyname, DNS_TSIG_GSSAPI_NAME,
+ dstkey, false, NULL,
+ rtkey.inception, rtkey.expire,
+ ring->mctx, ring, outkey));
+ dst_key_free(&dstkey);
+ dns_rdata_freestruct(&rtkey);
+ return (result);
+
+ failure:
+ /*
+ * XXXSRA This probably leaks memory from rtkey and qtkey.
+ */
+ if (dstkey != NULL)
+ dst_key_free(&dstkey);
+ 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,
+ bool win2k, char **err_message)
+{
+ dns_rdata_t rtkeyrdata = DNS_RDATA_INIT, qtkeyrdata = DNS_RDATA_INIT;
+ dns_name_t *tkeyname;
+ dns_rdata_tkey_t rtkey, qtkey, tkey;
+ isc_buffer_t intoken, outtoken;
+ dst_key_t *dstkey = NULL;
+ isc_result_t result;
+ unsigned char array[TEMP_BUFFER_SZ];
+ bool freertkey = false;
+
+ 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));
+ freertkey = true;
+
+ if (win2k == 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,
+ ring->mctx, err_message);
+ if (result != DNS_R_CONTINUE && result != ISC_R_SUCCESS)
+ return (result);
+
+ if (result == DNS_R_CONTINUE) {
+ dns_fixedname_t fixed;
+
+ dns_fixedname_init(&fixed);
+ dns_name_copy(tkeyname, dns_fixedname_name(&fixed), NULL);
+ tkeyname = dns_fixedname_name(&fixed);
+
+ 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);
+
+ tkey.inception = qtkey.inception;
+ tkey.expire = qtkey.expire;
+ tkey.mode = DNS_TKEYMODE_GSSAPI;
+ tkey.error = 0;
+ tkey.key = isc_buffer_base(&outtoken);
+ tkey.keylen = isc_buffer_usedlength(&outtoken);
+ tkey.other = NULL;
+ tkey.otherlen = 0;
+
+ dns_message_reset(qmsg, DNS_MESSAGE_INTENTRENDER);
+ RETERR(buildquery(qmsg, tkeyname, &tkey, win2k));
+ return (DNS_R_CONTINUE);
+ }
+
+ RETERR(dst_key_fromgssapi(dns_rootname, *context, rmsg->mctx,
+ &dstkey, NULL));
+
+ /*
+ * 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, true, NULL,
+ rtkey.inception, rtkey.expire,
+ ring->mctx, ring, outkey));
+ dst_key_free(&dstkey);
+ dns_rdata_freestruct(&rtkey);
+ return (result);
+
+ failure:
+ /*
+ * XXXSRA This probably leaks memory from qtkey.
+ */
+ if (freertkey)
+ dns_rdata_freestruct(&rtkey);
+ if (dstkey != NULL)
+ dst_key_free(&dstkey);
+ return (result);
+}
diff --git a/lib/dns/tsec.c b/lib/dns/tsec.c
new file mode 100644
index 0000000..c5eca0e
--- /dev/null
+++ b/lib/dns/tsec.c
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <config.h>
+
+#include <isc/mem.h>
+#include <isc/util.h>
+
+#include <pk11/site.h>
+
+#include <dns/tsec.h>
+#include <dns/tsig.h>
+#include <dns/result.h>
+
+#include <dst/dst.h>
+
+#define DNS_TSEC_MAGIC ISC_MAGIC('T', 's', 'e', 'c')
+#define DNS_TSEC_VALID(t) ISC_MAGIC_VALID(t, DNS_TSEC_MAGIC)
+
+/*%
+ * DNS Transaction Security object. We assume this is not shared by
+ * multiple threads, and so the structure does not contain a lock.
+ */
+struct dns_tsec {
+ unsigned int magic;
+ dns_tsectype_t type;
+ isc_mem_t *mctx;
+ union {
+ dns_tsigkey_t *tsigkey;
+ dst_key_t *key;
+ } ukey;
+};
+
+isc_result_t
+dns_tsec_create(isc_mem_t *mctx, dns_tsectype_t type, dst_key_t *key,
+ dns_tsec_t **tsecp)
+{
+ isc_result_t result;
+ dns_tsec_t *tsec;
+ dns_tsigkey_t *tsigkey = NULL;
+ dns_name_t *algname;
+
+ REQUIRE(mctx != NULL);
+ REQUIRE(tsecp != NULL && *tsecp == NULL);
+
+ tsec = isc_mem_get(mctx, sizeof(*tsec));
+ if (tsec == NULL)
+ return (ISC_R_NOMEMORY);
+
+ tsec->type = type;
+ tsec->mctx = mctx;
+
+ switch (type) {
+ case dns_tsectype_tsig:
+ switch (dst_key_alg(key)) {
+#ifndef PK11_MD5_DISABLE
+ case DST_ALG_HMACMD5:
+ algname = dns_tsig_hmacmd5_name;
+ break;
+#endif
+ case DST_ALG_HMACSHA1:
+ algname = dns_tsig_hmacsha1_name;
+ break;
+ case DST_ALG_HMACSHA224:
+ algname = dns_tsig_hmacsha224_name;
+ break;
+ case DST_ALG_HMACSHA256:
+ algname = dns_tsig_hmacsha256_name;
+ break;
+ case DST_ALG_HMACSHA384:
+ algname = dns_tsig_hmacsha384_name;
+ break;
+ case DST_ALG_HMACSHA512:
+ algname = dns_tsig_hmacsha512_name;
+ break;
+ default:
+ isc_mem_put(mctx, tsec, sizeof(*tsec));
+ return (DNS_R_BADALG);
+ }
+ result = dns_tsigkey_createfromkey(dst_key_name(key),
+ algname, key, false,
+ NULL, 0, 0, mctx, NULL,
+ &tsigkey);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(mctx, tsec, sizeof(*tsec));
+ return (result);
+ }
+ tsec->ukey.tsigkey = tsigkey;
+ break;
+ case dns_tsectype_sig0:
+ tsec->ukey.key = key;
+ break;
+ default:
+ INSIST(0);
+ }
+
+ tsec->magic = DNS_TSEC_MAGIC;
+
+ *tsecp = tsec;
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_tsec_destroy(dns_tsec_t **tsecp) {
+ dns_tsec_t *tsec;
+
+ REQUIRE(tsecp != NULL && *tsecp != NULL);
+ tsec = *tsecp;
+ REQUIRE(DNS_TSEC_VALID(tsec));
+
+ switch (tsec->type) {
+ case dns_tsectype_tsig:
+ dns_tsigkey_detach(&tsec->ukey.tsigkey);
+ break;
+ case dns_tsectype_sig0:
+ dst_key_free(&tsec->ukey.key);
+ break;
+ default:
+ INSIST(0);
+ }
+
+ tsec->magic = 0;
+ isc_mem_put(tsec->mctx, tsec, sizeof(*tsec));
+
+ *tsecp = NULL;
+}
+
+dns_tsectype_t
+dns_tsec_gettype(dns_tsec_t *tsec) {
+ REQUIRE(DNS_TSEC_VALID(tsec));
+
+ return (tsec->type);
+}
+
+void
+dns_tsec_getkey(dns_tsec_t *tsec, void *keyp) {
+ REQUIRE(DNS_TSEC_VALID(tsec));
+ REQUIRE(keyp != NULL);
+
+ switch (tsec->type) {
+ case dns_tsectype_tsig:
+ dns_tsigkey_attach(tsec->ukey.tsigkey, (dns_tsigkey_t **)keyp);
+ break;
+ case dns_tsectype_sig0:
+ *(dst_key_t **)keyp = tsec->ukey.key;
+ break;
+ default:
+ INSIST(0);
+ }
+}
diff --git a/lib/dns/tsig.c b/lib/dns/tsig.c
new file mode 100644
index 0000000..a94ec69
--- /dev/null
+++ b/lib/dns/tsig.c
@@ -0,0 +1,2019 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * $Id$
+ */
+/*! \file */
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include <isc/buffer.h>
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/refcount.h>
+#include <isc/serial.h>
+#include <isc/string.h> /* Required for HP/UX (and others?) */
+#include <isc/util.h>
+#include <isc/time.h>
+
+#include <pk11/site.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)
+
+#ifndef DNS_TSIG_MAXGENERATEDKEYS
+#define DNS_TSIG_MAXGENERATEDKEYS 4096
+#endif
+
+#define is_response(msg) (msg->flags & DNS_MESSAGEFLAG_QR)
+#ifndef PK11_MD5_DISABLE
+#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)
+#else
+#define algname_is_allocated(algname) \
+ ((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)
+#endif
+
+#define BADTIMELEN 6
+
+#ifndef PK11_MD5_DISABLE
+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_INITABSOLUTE(hmacmd5_ndata, hmacmd5_offsets);
+LIBDNS_EXTERNAL_DATA dns_name_t *dns_tsig_hmacmd5_name = &hmacmd5;
+#endif
+
+static unsigned char gsstsig_ndata[] = "\010gss-tsig";
+static unsigned char gsstsig_offsets[] = { 0, 9 };
+static dns_name_t gsstsig =
+ DNS_NAME_INITABSOLUTE(gsstsig_ndata, gsstsig_offsets);
+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_INITABSOLUTE(gsstsigms_ndata, gsstsigms_offsets);
+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_INITABSOLUTE(hmacsha1_ndata, hmacsha1_offsets);
+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_INITABSOLUTE(hmacsha224_ndata, hmacsha224_offsets);
+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_INITABSOLUTE(hmacsha256_ndata, hmacsha256_offsets);
+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_INITABSOLUTE(hmacsha384_ndata, hmacsha384_offsets);
+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_INITABSOLUTE(hmacsha512_ndata, hmacsha512_offsets);
+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) == false)
+ return;
+ if (key != NULL) {
+ dns_name_format(&key->name, namestr, sizeof(namestr));
+ } else {
+ strlcpy(namestr, "<null>", sizeof(namestr));
+ }
+
+ if (key != NULL && key->generated && key->creator) {
+ dns_name_format(key->creator, creatorstr, sizeof(creatorstr));
+ } else {
+ strlcpy(creatorstr, "<null>", 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);
+ }
+}
+
+static void
+remove_fromring(dns_tsigkey_t *tkey) {
+ if (tkey->generated) {
+ ISC_LIST_UNLINK(tkey->ring->lru, tkey, link);
+ tkey->ring->generated--;
+ }
+ (void)dns_rbt_deletename(tkey->ring->keys, &tkey->name, false);
+}
+
+static void
+adjust_lru(dns_tsigkey_t *tkey) {
+ if (tkey->generated) {
+ RWLOCK(&tkey->ring->lock, isc_rwlocktype_write);
+ /*
+ * We may have been removed from the LRU list between
+ * removing the read lock and aquiring the write lock.
+ */
+ if (ISC_LINK_LINKED(tkey, link) &&
+ tkey->ring->lru.tail != tkey)
+ {
+ ISC_LIST_UNLINK(tkey->ring->lru, tkey, link);
+ ISC_LIST_APPEND(tkey->ring->lru, tkey, link);
+ }
+ RWUNLOCK(&tkey->ring->lock, isc_rwlocktype_write);
+ }
+}
+
+/*
+ * A supplemental routine just to add a key to ring. Note that reference
+ * counter should be counted separately because we may be adding the key
+ * as part of creation of the key, in which case the reference counter was
+ * already initialized. Also note we don't need RWLOCK for the reference
+ * counter: it's protected by a separate lock.
+ */
+static isc_result_t
+keyring_add(dns_tsig_keyring_t *ring, dns_name_t *name,
+ dns_tsigkey_t *tkey)
+{
+ isc_result_t result;
+
+ 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;
+ }
+
+ result = dns_rbt_addname(ring->keys, name, tkey);
+ if (result == ISC_R_SUCCESS && tkey->generated) {
+ /*
+ * Add the new key to the LRU list and remove the least
+ * recently used key if there are too many keys on the list.
+ */
+ ISC_LIST_APPEND(ring->lru, tkey, link);
+ if (ring->generated++ > ring->maxgenerated)
+ remove_fromring(ISC_LIST_HEAD(ring->lru));
+ }
+ RWUNLOCK(&ring->lock, isc_rwlocktype_write);
+
+ return (result);
+}
+
+isc_result_t
+dns_tsigkey_createfromkey(dns_name_t *name, dns_name_t *algorithm,
+ dst_key_t *dstkey, bool 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);
+
+#ifndef PK11_MD5_DISABLE
+ 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
+#endif
+ 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 = NULL;
+ if (dstkey != NULL)
+ dst_key_attach(dstkey, &tkey->key);
+ tkey->ring = ring;
+
+ if (key != NULL)
+ refs = 1;
+ 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);
+ ISC_LINK_INIT(tkey, link);
+
+ tkey->magic = TSIG_MAGIC;
+
+ if (ring != NULL) {
+ ret = keyring_add(ring, name, tkey);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_refs;
+ }
+
+ /*
+ * 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->key != NULL)
+ dst_key_free(&tkey->key);
+ 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);
+ origin = dns_fixedname_initname(&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);
+ remove_fromring(tkey);
+ goto again;
+ }
+ }
+ result = dns_rbtnodechain_next(&chain, &foundname,
+ origin);
+ if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
+ dns_rbtnodechain_invalidate(&chain);
+ return;
+ }
+ }
+}
+
+static void
+destroyring(dns_tsig_keyring_t *ring) {
+ dns_rbt_destroy(&ring->keys);
+ isc_rwlock_destroy(&ring->lock);
+ isc_mem_putanddetach(&ring->mctx, ring, sizeof(dns_tsig_keyring_t));
+}
+
+static unsigned int
+dst_alg_fromname(dns_name_t *algorithm) {
+#ifndef PK11_MD5_DISABLE
+ if (dns_name_equal(algorithm, DNS_TSIG_HMACMD5_NAME)) {
+ return (DST_ALG_HMACMD5);
+ } else
+#endif
+ if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA1_NAME)) {
+ return (DST_ALG_HMACSHA1);
+ } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA224_NAME)) {
+ return (DST_ALG_HMACSHA224);
+ } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA256_NAME)) {
+ return (DST_ALG_HMACSHA256);
+ } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA384_NAME)) {
+ return (DST_ALG_HMACSHA384);
+ } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA512_NAME)) {
+ return (DST_ALG_HMACSHA512);
+ } else if (dns_name_equal(algorithm, DNS_TSIG_GSSAPI_NAME)) {
+ return (DST_ALG_GSSAPI);
+ } else if (dns_name_equal(algorithm, DNS_TSIG_GSSAPIMS_NAME)) {
+ return (DST_ALG_GSSAPI);
+ } else
+ return (0);
+}
+
+static isc_result_t
+restore_key(dns_tsig_keyring_t *ring, isc_stdtime_t now, FILE *fp) {
+ dst_key_t *dstkey = NULL;
+ char namestr[1024];
+ char creatorstr[1024];
+ char algorithmstr[1024];
+ char keystr[4096];
+ unsigned int inception, expire;
+ int n;
+ isc_buffer_t b;
+ dns_name_t *name, *creator, *algorithm;
+ dns_fixedname_t fname, fcreator, falgorithm;
+ isc_result_t result;
+ unsigned int dstalg;
+
+ n = fscanf(fp, "%1023s %1023s %u %u %1023s %4095s\n", namestr,
+ creatorstr, &inception, &expire, algorithmstr, keystr);
+ if (n == EOF)
+ return (ISC_R_NOMORE);
+ if (n != 6)
+ return (ISC_R_FAILURE);
+
+ if (isc_serial_lt(expire, now))
+ return (DNS_R_EXPIRED);
+
+ name = dns_fixedname_initname(&fname);
+ isc_buffer_init(&b, namestr, strlen(namestr));
+ isc_buffer_add(&b, strlen(namestr));
+ result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ creator = dns_fixedname_initname(&fcreator);
+ isc_buffer_init(&b, creatorstr, strlen(creatorstr));
+ isc_buffer_add(&b, strlen(creatorstr));
+ result = dns_name_fromtext(creator, &b, dns_rootname, 0, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ algorithm = dns_fixedname_initname(&falgorithm);
+ isc_buffer_init(&b, algorithmstr, strlen(algorithmstr));
+ isc_buffer_add(&b, strlen(algorithmstr));
+ result = dns_name_fromtext(algorithm, &b, dns_rootname, 0, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ dstalg = dst_alg_fromname(algorithm);
+ if (dstalg == 0)
+ return (DNS_R_BADALG);
+
+ result = dst_key_restore(name, dstalg, DNS_KEYOWNER_ENTITY,
+ DNS_KEYPROTO_DNSSEC, dns_rdataclass_in,
+ ring->mctx, keystr, &dstkey);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = dns_tsigkey_createfromkey(name, algorithm, dstkey,
+ true, creator, inception,
+ expire, ring->mctx, ring, NULL);
+ if (dstkey != NULL)
+ dst_key_free(&dstkey);
+ return (result);
+}
+
+static void
+dump_key(dns_tsigkey_t *tkey, FILE *fp) {
+ char *buffer = NULL;
+ int length = 0;
+ char namestr[DNS_NAME_FORMATSIZE];
+ char creatorstr[DNS_NAME_FORMATSIZE];
+ char algorithmstr[DNS_NAME_FORMATSIZE];
+ isc_result_t result;
+
+ REQUIRE(tkey != NULL);
+ REQUIRE(fp != NULL);
+
+ dns_name_format(&tkey->name, namestr, sizeof(namestr));
+ dns_name_format(tkey->creator, creatorstr, sizeof(creatorstr));
+ dns_name_format(tkey->algorithm, algorithmstr, sizeof(algorithmstr));
+ result = dst_key_dump(tkey->key, tkey->mctx, &buffer, &length);
+ if (result == ISC_R_SUCCESS)
+ fprintf(fp, "%s %s %u %u %s %.*s\n", namestr, creatorstr,
+ tkey->inception, tkey->expire, algorithmstr,
+ length, buffer);
+ if (buffer != NULL)
+ isc_mem_put(tkey->mctx, buffer, length);
+}
+
+isc_result_t
+dns_tsigkeyring_dumpanddetach(dns_tsig_keyring_t **ringp, FILE *fp) {
+ 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;
+ dns_tsig_keyring_t *ring;
+ unsigned int references;
+
+ REQUIRE(ringp != NULL && *ringp != NULL);
+
+ ring = *ringp;
+ *ringp = NULL;
+
+ RWLOCK(&ring->lock, isc_rwlocktype_write);
+ INSIST(ring->references > 0);
+ ring->references--;
+ references = ring->references;
+ RWUNLOCK(&ring->lock, isc_rwlocktype_write);
+
+ if (references != 0)
+ return (DNS_R_CONTINUE);
+
+ isc_stdtime_get(&now);
+ dns_name_init(&foundname, NULL);
+ origin = dns_fixedname_initname(&fixedorigin);
+ 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);
+ goto destroy;
+ }
+
+ for (;;) {
+ node = NULL;
+ dns_rbtnodechain_current(&chain, &foundname, origin, &node);
+ tkey = node->data;
+ if (tkey != NULL && tkey->generated && tkey->expire >= now)
+ dump_key(tkey, fp);
+ result = dns_rbtnodechain_next(&chain, &foundname,
+ origin);
+ if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
+ dns_rbtnodechain_invalidate(&chain);
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ goto destroy;
+ }
+ }
+
+ destroy:
+ destroyring(ring);
+ return (result);
+}
+
+isc_result_t
+dns_tsigkey_create(dns_name_t *name, dns_name_t *algorithm,
+ unsigned char *secret, int length, bool 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);
+
+#ifndef PK11_MD5_DISABLE
+ 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
+#endif
+ 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 (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);
+ remove_fromring(key);
+ 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;
+ bool response;
+
+ REQUIRE(msg != NULL);
+ key = dns_message_gettsigkey(msg);
+ REQUIRE(VALID_TSIG_KEY(key));
+
+ /*
+ * If this is a response, there should be a query tsig.
+ */
+ response = is_response(msg);
+ if (response && msg->querytsig == NULL)
+ return (DNS_R_EXPECTEDTSIG);
+
+ dynbuf = NULL;
+
+ mctx = msg->mctx;
+
+ 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 (response)
+ 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) &&
+ (tsig.error != dns_tsigerror_badkey))
+ {
+ unsigned char header[DNS_MESSAGE_HEADERLEN];
+ isc_buffer_t headerbuf;
+ uint16_t digestbits;
+
+ /*
+ * If it is a response, we assume that the request MAC
+ * has validated at this point. This is why we include a
+ * MAC length > 0 in the reply.
+ */
+ ret = dst_context_create3(key->key, mctx,
+ DNS_LOGCATEGORY_DNSSEC,
+ true, &ctx);
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+
+ /*
+ * If this is a response, digest the request's MAC.
+ */
+ if (response) {
+ dns_rdata_t querytsigrdata = DNS_RDATA_INIT;
+
+ INSIST(msg->verified_sig);
+
+ 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;
+ }
+#if defined(__clang__) && \
+ ( __clang_major__ < 3 || \
+ (__clang_major__ == 3 && __clang_minor__ < 2) || \
+ (__clang_major__ == 4 && __clang_minor__ < 2))
+ /* false positive: http://llvm.org/bugs/show_bug.cgi?id=14461 */
+ else memset(&querytsig, 0, sizeof(querytsig));
+#endif
+
+ /*
+ * 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) {
+ INSIST(response);
+ 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 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) {
+ /*
+ * XXXRAY: Is this correct? What is the
+ * expected behavior when digestbits is not an
+ * integral multiple of 8? It looks like bytes
+ * should either be (digestbits/8) or
+ * (digestbits+7)/8.
+ *
+ * In any case, for current algorithms,
+ * digestbits are an integral multiple of 8, so
+ * it has the same effect as (digestbits/8).
+ */
+ unsigned int bytes = (digestbits + 1) / 8;
+ if (response && 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;
+ ISC_LIST_APPEND(datalist->rdata, rdata, link);
+ RUNTIME_CHECK(dns_rdatalist_tordataset(datalist, dataset)
+ == ISC_R_SUCCESS);
+ msg->tsig = dataset;
+ msg->tsigname = owner;
+
+ /* Windows does not like the tsig name being compressed. */
+ msg->tsigname->attributes |= DNS_NAMEATTR_NOCOMPRESS;
+
+ 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;
+ uint16_t addcount, id;
+ unsigned int siglen;
+ unsigned int alg;
+ bool response;
+
+ REQUIRE(source != NULL);
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ tsigkey = dns_message_gettsigkey(msg);
+ response = is_response(msg);
+
+ REQUIRE(tsigkey == NULL || VALID_TSIG_KEY(tsigkey));
+
+ msg->verify_attempted = 1;
+ msg->verified_sig = 0;
+ msg->tsigstatus = dns_tsigerror_badsig;
+
+ 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 (response && (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 (response) {
+ 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);
+ }
+#if defined(__clang__) && \
+ ( __clang_major__ < 3 || \
+ (__clang_major__ == 3 && __clang_minor__ < 2) || \
+ (__clang_major__ == 4 && __clang_minor__ < 2))
+ /* false positive: http://llvm.org/bugs/show_bug.cgi?id=14461 */
+ else memset(&querytsig, 0, sizeof(querytsig));
+#endif
+
+ /*
+ * Do the key name and algorithm match that of the query?
+ */
+ if (response &&
+ (!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, 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;
+
+ /*
+ * Check digest length.
+ */
+ alg = dst_key_alg(key);
+ ret = dst_key_sigsize(key, &siglen);
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+ if (
+#ifndef PK11_MD5_DISABLE
+ alg == DST_ALG_HMACMD5 ||
+#endif
+ alg == DST_ALG_HMACSHA1 ||
+ alg == DST_ALG_HMACSHA224 || alg == DST_ALG_HMACSHA256 ||
+ alg == DST_ALG_HMACSHA384 || alg == DST_ALG_HMACSHA512)
+ {
+ if (tsig.siglen > siglen) {
+ tsig_log(msg->tsigkey, 2, "signature length too 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) {
+ uint16_t addcount_n;
+
+ sig_r.base = tsig.signature;
+ sig_r.length = tsig.siglen;
+
+ ret = dst_context_create3(key, mctx,
+ DNS_LOGCATEGORY_DNSSEC,
+ false, &ctx);
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+
+ if (response) {
+ 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);
+ memmove(header, r.base, DNS_MESSAGE_HEADERLEN);
+ isc_region_consume(&r, DNS_MESSAGE_HEADERLEN);
+
+ /*
+ * Decrement the additional field counter.
+ */
+ memmove(&addcount, &header[DNS_MESSAGE_HEADERLEN - 2], 2);
+ addcount_n = ntohs(addcount);
+ addcount = htons((uint16_t)(addcount_n - 1));
+ memmove(&header[DNS_MESSAGE_HEADERLEN - 2], &addcount, 2);
+
+ /*
+ * Put in the original id.
+ */
+ id = htons(tsig.originalid);
+ memmove(&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) {
+ 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;
+ }
+ msg->verified_sig = 1;
+ } else if (tsig.error != dns_tsigerror_badsig &&
+ tsig.error != dns_tsigerror_badkey) {
+ tsig_log(msg->tsigkey, 2, "signature was empty");
+ return (DNS_R_TSIGVERIFYFAILURE);
+ }
+
+ /*
+ * Here at this point, the MAC has been verified. Even if any of
+ * the following code returns a TSIG error, the reply will be
+ * signed and WILL always include the request MAC in the digest
+ * computation.
+ */
+
+ /*
+ * 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");
+ ret = DNS_R_CLOCKSKEW;
+ goto cleanup_context;
+ } 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_context;
+ }
+
+ if (
+#ifndef PK11_MD5_DISABLE
+ alg == DST_ALG_HMACMD5 ||
+#endif
+ alg == DST_ALG_HMACSHA1 ||
+ alg == DST_ALG_HMACSHA224 || alg == DST_ALG_HMACSHA256 ||
+ alg == DST_ALG_HMACSHA384 || alg == DST_ALG_HMACSHA512)
+ {
+ uint16_t digestbits = dst_key_getbits(key);
+
+ /*
+ * XXXRAY: Is this correct? What is the expected
+ * behavior when digestbits is not an integral multiple
+ * of 8? It looks like bytes should either be
+ * (digestbits/8) or (digestbits+7)/8.
+ *
+ * In any case, for current algorithms, digestbits are
+ * an integral multiple of 8, so it has the same effect
+ * as (digestbits/8).
+ */
+ 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");
+ ret = DNS_R_TSIGVERIFYFAILURE;
+ goto cleanup_context;
+ }
+ if (tsig.siglen > 0 && digestbits == 0 &&
+ tsig.siglen < siglen)
+ {
+ msg->tsigstatus = dns_tsigerror_badtrunc;
+ tsig_log(msg->tsigkey, 2, "signature length too small");
+ ret = DNS_R_TSIGVERIFYFAILURE;
+ goto cleanup_context;
+ }
+ }
+
+ if (tsig.error != dns_rcode_noerror) {
+ msg->tsigstatus = tsig.error;
+ if (tsig.error == dns_tsigerror_badtime)
+ ret = DNS_R_CLOCKSKEW;
+ else
+ ret = DNS_R_TSIGERRORSET;
+ goto cleanup_context;
+ }
+
+ msg->tsigstatus = dns_rcode_noerror;
+ ret = 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];
+ uint16_t addcount, id;
+ bool has_tsig = false;
+ isc_mem_t *mctx;
+ unsigned int siglen;
+ unsigned int alg;
+
+ REQUIRE(source != NULL);
+ REQUIRE(msg != NULL);
+ REQUIRE(dns_message_gettsigkey(msg) != NULL);
+ REQUIRE(msg->tcp_continuation == 1);
+ REQUIRE(msg->querytsig != NULL);
+
+ msg->verified_sig = 0;
+ msg->tsigstatus = dns_tsigerror_badsig;
+
+ if (!is_response(msg))
+ return (DNS_R_EXPECTEDRESPONSE);
+
+ mctx = msg->mctx;
+
+ tsigkey = dns_message_gettsigkey(msg);
+ key = tsigkey->key;
+
+ /*
+ * 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 = 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;
+ }
+
+ /*
+ * Check digest length.
+ */
+ alg = dst_key_alg(key);
+ ret = dst_key_sigsize(key, &siglen);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_querystruct;
+ if (
+#ifndef PK11_MD5_DISABLE
+ alg == DST_ALG_HMACMD5 ||
+#endif
+ alg == DST_ALG_HMACSHA1 ||
+ alg == DST_ALG_HMACSHA224 ||
+ alg == DST_ALG_HMACSHA256 ||
+ alg == DST_ALG_HMACSHA384 ||
+ alg == DST_ALG_HMACSHA512)
+ {
+ if (tsig.siglen > siglen) {
+ tsig_log(tsigkey, 2,
+ "signature length too big");
+ ret = DNS_R_FORMERR;
+ goto cleanup_querystruct;
+ }
+ if (tsig.siglen > 0 &&
+ (tsig.siglen < 10 ||
+ tsig.siglen < ((siglen + 1) / 2)))
+ {
+ tsig_log(tsigkey, 2,
+ "signature length below minimum");
+ ret = DNS_R_FORMERR;
+ goto cleanup_querystruct;
+ }
+ }
+ }
+
+ if (msg->tsigctx == NULL) {
+ ret = dst_context_create3(key, mctx,
+ DNS_LOGCATEGORY_DNSSEC,
+ false, &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);
+ memmove(header, r.base, DNS_MESSAGE_HEADERLEN);
+ isc_region_consume(&r, DNS_MESSAGE_HEADERLEN);
+
+ /*
+ * Decrement the additional field counter if necessary.
+ */
+ if (has_tsig) {
+ uint16_t addcount_n;
+
+ memmove(&addcount, &header[DNS_MESSAGE_HEADERLEN - 2], 2);
+ addcount_n = ntohs(addcount);
+ addcount = htons((uint16_t)(addcount_n - 1));
+ memmove(&header[DNS_MESSAGE_HEADERLEN - 2], &addcount, 2);
+
+ /*
+ * Put in the original id.
+ *
+ * XXX Can TCP transfers be forwarded? How would that
+ * work?
+ */
+ id = htons(tsig.originalid);
+ memmove(&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) {
+ msg->tsigstatus = tsig.error;
+ 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) {
+ 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;
+ }
+ msg->verified_sig = 1;
+
+ /*
+ * Here at this point, the MAC has been verified. Even
+ * if any of the following code returns a TSIG error,
+ * the reply will be signed and WILL always include the
+ * request MAC in the digest computation.
+ */
+
+ /*
+ * 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_context;
+ } 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_context;
+ }
+
+ alg = dst_key_alg(key);
+ ret = dst_key_sigsize(key, &siglen);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_context;
+ if (
+#ifndef PK11_MD5_DISABLE
+ alg == DST_ALG_HMACMD5 ||
+#endif
+ alg == DST_ALG_HMACSHA1 ||
+ alg == DST_ALG_HMACSHA224 ||
+ alg == DST_ALG_HMACSHA256 ||
+ alg == DST_ALG_HMACSHA384 ||
+ alg == DST_ALG_HMACSHA512)
+ {
+ uint16_t digestbits = dst_key_getbits(key);
+
+ /*
+ * XXXRAY: Is this correct? What is the
+ * expected behavior when digestbits is not an
+ * integral multiple of 8? It looks like bytes
+ * should either be (digestbits/8) or
+ * (digestbits+7)/8.
+ *
+ * In any case, for current algorithms,
+ * digestbits are an integral multiple of 8, so
+ * it has the same effect as (digestbits/8).
+ */
+ 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");
+ ret = DNS_R_TSIGVERIFYFAILURE;
+ goto cleanup_context;
+ }
+ if (tsig.siglen > 0 && digestbits == 0 &&
+ tsig.siglen < siglen)
+ {
+ msg->tsigstatus = dns_tsigerror_badtrunc;
+ tsig_log(msg->tsigkey, 2,
+ "signature length too small");
+ ret = DNS_R_TSIGVERIFYFAILURE;
+ goto cleanup_context;
+ }
+ }
+
+ if (tsig.error != dns_rcode_noerror) {
+ msg->tsigstatus = tsig.error;
+ if (tsig.error == dns_tsigerror_badtime)
+ ret = DNS_R_CLOCKSKEW;
+ else
+ ret = DNS_R_TSIGERRORSET;
+ goto cleanup_context;
+ }
+ }
+
+ msg->tsigstatus = dns_rcode_noerror;
+ ret = ISC_R_SUCCESS;
+
+ cleanup_context:
+ /*
+ * Except in error conditions, don't destroy the DST context
+ * for unsigned messages; it is a running sum till the next
+ * TSIG signed message.
+ */
+ if ((ret != ISC_R_SUCCESS || has_tsig) && msg->tsigctx != NULL) {
+ 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 && isc_serial_lt(key->expire, now)) {
+ /*
+ * The key has expired.
+ */
+ RWUNLOCK(&ring->lock, isc_rwlocktype_read);
+ RWLOCK(&ring->lock, isc_rwlocktype_write);
+ remove_fromring(key);
+ RWUNLOCK(&ring->lock, isc_rwlocktype_write);
+ return (ISC_R_NOTFOUND);
+ }
+#if 0
+ /*
+ * MPAXXX We really should look at the inception time.
+ */
+ if (key->inception != key->expire &&
+ isc_serial_lt(key->inception, now)) {
+ RWUNLOCK(&ring->lock, isc_rwlocktype_read);
+ adjust_lru(key);
+ return (ISC_R_NOTFOUND);
+ }
+#endif
+ isc_refcount_increment(&key->refs, NULL);
+ RWUNLOCK(&ring->lock, isc_rwlocktype_read);
+ adjust_lru(key);
+ *tsigkey = key;
+ return (ISC_R_SUCCESS);
+}
+
+static void
+free_tsignode(void *node, void *_unused) {
+ dns_tsigkey_t *key;
+
+ REQUIRE(node != NULL);
+
+ UNUSED(_unused);
+
+ key = node;
+ if (key->generated) {
+ if (ISC_LINK_LINKED(key, link))
+ ISC_LIST_UNLINK(key->ring->lru, key, link);
+ }
+ 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;
+ ring->generated = 0;
+ ring->maxgenerated = DNS_TSIG_MAXGENERATEDKEYS;
+ ISC_LIST_INIT(ring->lru);
+ isc_mem_attach(mctx, &ring->mctx);
+ ring->references = 1;
+
+ *ringp = ring;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_tsigkeyring_add(dns_tsig_keyring_t *ring, dns_name_t *name,
+ dns_tsigkey_t *tkey)
+{
+ isc_result_t result;
+
+ result = keyring_add(ring, name, tkey);
+ if (result == ISC_R_SUCCESS)
+ isc_refcount_increment(&tkey->refs, NULL);
+
+ return (result);
+}
+
+void
+dns_tsigkeyring_attach(dns_tsig_keyring_t *source, dns_tsig_keyring_t **target)
+{
+ REQUIRE(source != NULL);
+ REQUIRE(target != NULL && *target == NULL);
+
+ RWLOCK(&source->lock, isc_rwlocktype_write);
+ INSIST(source->references > 0);
+ source->references++;
+ INSIST(source->references > 0);
+ *target = source;
+ RWUNLOCK(&source->lock, isc_rwlocktype_write);
+}
+
+void
+dns_tsigkeyring_detach(dns_tsig_keyring_t **ringp) {
+ dns_tsig_keyring_t *ring;
+ unsigned int references;
+
+ REQUIRE(ringp != NULL);
+ REQUIRE(*ringp != NULL);
+
+ ring = *ringp;
+ *ringp = NULL;
+
+ RWLOCK(&ring->lock, isc_rwlocktype_write);
+ INSIST(ring->references > 0);
+ ring->references--;
+ references = ring->references;
+ RWUNLOCK(&ring->lock, isc_rwlocktype_write);
+
+ if (references == 0)
+ destroyring(ring);
+}
+
+void
+dns_keyring_restore(dns_tsig_keyring_t *ring, FILE *fp) {
+ isc_stdtime_t now;
+ isc_result_t result;
+
+ isc_stdtime_get(&now);
+ do {
+ result = restore_key(ring, now, fp);
+ if (result == ISC_R_NOMORE)
+ return;
+ if (result == DNS_R_BADALG || result == DNS_R_EXPIRED)
+ result = ISC_R_SUCCESS;
+ } while (result == ISC_R_SUCCESS);
+}
diff --git a/lib/dns/ttl.c b/lib/dns/ttl.c
new file mode 100644
index 0000000..de2f9dc
--- /dev/null
+++ b/lib/dns/ttl.c
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdbool.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, uint32_t *ttl);
+
+/*
+ * Helper for dns_ttl_totext().
+ */
+static isc_result_t
+ttlfmt(unsigned int t, const char *s, bool verbose,
+ bool space, isc_buffer_t *target)
+{
+ char tmp[60];
+ unsigned int 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);
+ memmove(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(uint32_t src, bool verbose, isc_buffer_t *target) {
+ return (dns_ttl_totext2(src, verbose, true, target));
+}
+
+isc_result_t
+dns_ttl_totext2(uint32_t src, bool verbose,
+ bool upcase, 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;
+ POST(src);
+
+ x = 0;
+ if (weeks != 0) {
+ RETERR(ttlfmt(weeks, "week", verbose, (x > 0), target));
+ x++;
+ }
+ if (days != 0) {
+ RETERR(ttlfmt(days, "day", verbose, (x > 0), target));
+ x++;
+ }
+ if (hours != 0) {
+ RETERR(ttlfmt(hours, "hour", verbose, (x > 0), target));
+ x++;
+ }
+ if (mins != 0) {
+ RETERR(ttlfmt(mins, "minute", verbose, (x > 0), target));
+ x++;
+ }
+ if (secs != 0 ||
+ (weeks == 0 && days == 0 && hours == 0 && mins == 0)) {
+ RETERR(ttlfmt(secs, "second", verbose, (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 && upcase && !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, uint32_t *ttl) {
+ return (bind_ttl(source, ttl));
+}
+
+isc_result_t
+dns_ttl_fromtext(isc_textregion_t *source, uint32_t *ttl) {
+ isc_result_t result;
+
+ result = bind_ttl(source, ttl);
+ if (result != ISC_R_SUCCESS && result != ISC_R_RANGE)
+ result = DNS_R_BADTTL;
+ return (result);
+}
+
+static isc_result_t
+bind_ttl(isc_textregion_t *source, uint32_t *ttl) {
+ uint64_t tmp = 0ULL;
+ 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);
+ /* Copy source->length bytes and NUL terminate. */
+ snprintf(buf, sizeof(buf), "%.*s", (int)source->length, source->base);
+ 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 += (uint64_t) n * 7 * 24 * 3600;
+ s++;
+ break;
+ case 'd':
+ case 'D':
+ tmp += (uint64_t) n * 24 * 3600;
+ s++;
+ break;
+ case 'h':
+ case 'H':
+ tmp += (uint64_t) n * 3600;
+ s++;
+ break;
+ case 'm':
+ case 'M':
+ tmp += (uint64_t) n * 60;
+ s++;
+ break;
+ case 's':
+ case 'S':
+ tmp += (uint64_t) n;
+ s++;
+ break;
+ case '\0':
+ /* Plain number? */
+ if (tmp != 0ULL)
+ return (DNS_R_SYNTAX);
+ tmp = n;
+ break;
+ default:
+ return (DNS_R_SYNTAX);
+ }
+ } while (*s != '\0');
+
+ if (tmp > 0xffffffffULL)
+ return (ISC_R_RANGE);
+
+ *ttl = (uint32_t)(tmp & 0xffffffffUL);
+ return (ISC_R_SUCCESS);
+}
diff --git a/lib/dns/update.c b/lib/dns/update.c
new file mode 100644
index 0000000..1cc5f88
--- /dev/null
+++ b/lib/dns/update.c
@@ -0,0 +1,2077 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <time.h>
+
+#include <isc/log.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/netaddr.h>
+#include <isc/platform.h>
+#include <isc/print.h>
+#include <isc/serial.h>
+#include <isc/stats.h>
+#include <isc/stdtime.h>
+#include <isc/string.h>
+#include <isc/taskpool.h>
+#include <isc/time.h>
+#include <isc/util.h>
+
+#include <dns/db.h>
+#include <dns/dbiterator.h>
+#include <dns/diff.h>
+#include <dns/dnssec.h>
+#include <dns/events.h>
+#include <dns/fixedname.h>
+#include <dns/journal.h>
+#include <dns/keyvalues.h>
+#include <dns/log.h>
+#include <dns/message.h>
+#include <dns/nsec.h>
+#include <dns/nsec3.h>
+#include <dns/private.h>
+#include <dns/rdataclass.h>
+#include <dns/rdataset.h>
+#include <dns/rdatasetiter.h>
+#include <dns/rdatastruct.h>
+#include <dns/rdatatype.h>
+#include <dns/result.h>
+#include <dns/soa.h>
+#include <dns/ssu.h>
+#include <dns/tsig.h>
+#include <dns/update.h>
+#include <dns/view.h>
+#include <dns/zone.h>
+#include <dns/zt.h>
+
+
+/**************************************************************************/
+
+#define STATE_MAGIC ISC_MAGIC('S', 'T', 'T', 'E')
+#define DNS_STATE_VALID(state) ISC_MAGIC_VALID(state, STATE_MAGIC)
+
+/*%
+ * Log level for tracing dynamic update protocol requests.
+ */
+#define LOGLEVEL_PROTOCOL ISC_LOG_INFO
+
+/*%
+ * Log level for low-level debug tracing.
+ */
+#define LOGLEVEL_DEBUG ISC_LOG_DEBUG(8)
+
+/*%
+ * Check an operation for failure. These macros all assume that
+ * the function using them has a 'result' variable and a 'failure'
+ * label.
+ */
+#define CHECK(op) \
+ do { result = (op); \
+ if (result != ISC_R_SUCCESS) goto failure; \
+ } while (0)
+
+/*%
+ * Fail unconditionally with result 'code', which must not
+ * be ISC_R_SUCCESS. The reason for failure presumably has
+ * been logged already.
+ *
+ * The test against ISC_R_SUCCESS 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)
+
+/*%
+ * Fail unconditionally and log as a client error.
+ * The test against ISC_R_SUCCESS is there to keep the Solaris compiler
+ * from complaining about "end-of-loop code not reached".
+ */
+#define FAILC(code, msg) \
+ do { \
+ const char *_what = "failed"; \
+ result = (code); \
+ switch (result) { \
+ case DNS_R_NXDOMAIN: \
+ case DNS_R_YXDOMAIN: \
+ case DNS_R_YXRRSET: \
+ case DNS_R_NXRRSET: \
+ _what = "unsuccessful"; \
+ } \
+ update_log(log, zone, LOGLEVEL_PROTOCOL, \
+ "update %s: %s (%s)", _what, \
+ msg, isc_result_totext(result)); \
+ if (result != ISC_R_SUCCESS) goto failure; \
+ } while (0)
+
+#define FAILN(code, name, msg) \
+ do { \
+ const char *_what = "failed"; \
+ result = (code); \
+ switch (result) { \
+ case DNS_R_NXDOMAIN: \
+ case DNS_R_YXDOMAIN: \
+ case DNS_R_YXRRSET: \
+ case DNS_R_NXRRSET: \
+ _what = "unsuccessful"; \
+ } \
+ if (isc_log_wouldlog(dns_lctx, LOGLEVEL_PROTOCOL)) { \
+ char _nbuf[DNS_NAME_FORMATSIZE]; \
+ dns_name_format(name, _nbuf, sizeof(_nbuf)); \
+ update_log(log, zone, LOGLEVEL_PROTOCOL, \
+ "update %s: %s: %s (%s)", _what, _nbuf, \
+ msg, isc_result_totext(result)); \
+ } \
+ if (result != ISC_R_SUCCESS) goto failure; \
+ } while (0)
+
+#define FAILNT(code, name, type, msg) \
+ do { \
+ const char *_what = "failed"; \
+ result = (code); \
+ switch (result) { \
+ case DNS_R_NXDOMAIN: \
+ case DNS_R_YXDOMAIN: \
+ case DNS_R_YXRRSET: \
+ case DNS_R_NXRRSET: \
+ _what = "unsuccessful"; \
+ } \
+ if (isc_log_wouldlog(dns_lctx, LOGLEVEL_PROTOCOL)) { \
+ char _nbuf[DNS_NAME_FORMATSIZE]; \
+ char _tbuf[DNS_RDATATYPE_FORMATSIZE]; \
+ dns_name_format(name, _nbuf, sizeof(_nbuf)); \
+ dns_rdatatype_format(type, _tbuf, sizeof(_tbuf)); \
+ update_log(log, zone, LOGLEVEL_PROTOCOL, \
+ "update %s: %s/%s: %s (%s)", \
+ _what, _nbuf, _tbuf, msg, \
+ isc_result_totext(result)); \
+ } \
+ if (result != ISC_R_SUCCESS) goto failure; \
+ } while (0)
+
+/*%
+ * Fail unconditionally and log as a server error.
+ * The test against ISC_R_SUCCESS is there to keep the Solaris compiler
+ * from complaining about "end-of-loop code not reached".
+ */
+#define FAILS(code, msg) \
+ do { \
+ result = (code); \
+ update_log(log, zone, LOGLEVEL_PROTOCOL, \
+ "error: %s: %s", \
+ msg, isc_result_totext(result)); \
+ if (result != ISC_R_SUCCESS) goto failure; \
+ } while (0)
+
+/**************************************************************************/
+
+typedef struct rr rr_t;
+
+struct rr {
+ /* dns_name_t name; */
+ uint32_t ttl;
+ dns_rdata_t rdata;
+};
+
+typedef struct update_event update_event_t;
+
+/**************************************************************************/
+
+static void
+update_log(dns_update_log_t *callback, dns_zone_t *zone,
+ int level, const char *fmt, ...) ISC_FORMAT_PRINTF(4, 5);
+
+static void
+update_log(dns_update_log_t *callback, dns_zone_t *zone,
+ int level, const char *fmt, ...)
+{
+ va_list ap;
+ char message[4096];
+
+ if (callback == NULL)
+ return;
+
+ if (isc_log_wouldlog(dns_lctx, level) == false)
+ return;
+
+
+ va_start(ap, fmt);
+ vsnprintf(message, sizeof(message), fmt, ap);
+ va_end(ap);
+
+ (callback->func)(callback->arg, zone, level, message);
+}
+
+/*%
+ * 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);
+ 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
+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));
+}
+
+/**************************************************************************/
+/*
+ * Callback-style iteration over rdatasets and rdatas.
+ *
+ * foreach_rrset() can be used to iterate over the RRsets
+ * of a name and call a callback function with each
+ * one. Similarly, foreach_rr() can be used to iterate
+ * over the individual RRs at name, optionally restricted
+ * to RRs of a given type.
+ *
+ * The callback functions are called "actions" and take
+ * two arguments: a void pointer for passing arbitrary
+ * context information, and a pointer to the current RRset
+ * or RR. By convention, their names end in "_action".
+ */
+
+/*
+ * XXXRTH We might want to make this public somewhere in libdns.
+ */
+
+/*%
+ * Function type for foreach_rrset() iterator actions.
+ */
+typedef isc_result_t rrset_func(void *data, dns_rdataset_t *rrset);
+
+/*%
+ * Function type for foreach_rr() iterator actions.
+ */
+typedef isc_result_t rr_func(void *data, rr_t *rr);
+
+/*%
+ * Internal context struct for foreach_node_rr().
+ */
+typedef struct {
+ rr_func * rr_action;
+ void * rr_action_data;
+} foreach_node_rr_ctx_t;
+
+/*%
+ * Internal helper function for foreach_node_rr().
+ */
+static isc_result_t
+foreach_node_rr_action(void *data, dns_rdataset_t *rdataset) {
+ isc_result_t result;
+ foreach_node_rr_ctx_t *ctx = data;
+ for (result = dns_rdataset_first(rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(rdataset))
+ {
+ rr_t rr = { 0, DNS_RDATA_INIT };
+
+ dns_rdataset_current(rdataset, &rr.rdata);
+ rr.ttl = rdataset->ttl;
+ result = (*ctx->rr_action)(ctx->rr_action_data, &rr);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+ if (result != ISC_R_NOMORE)
+ return (result);
+ return (ISC_R_SUCCESS);
+}
+
+/*%
+ * For each rdataset of 'name' in 'ver' of 'db', call 'action'
+ * with the rdataset and 'action_data' as arguments. If the name
+ * does not exist, do nothing.
+ *
+ * If 'action' returns an error, abort iteration and return the error.
+ */
+static isc_result_t
+foreach_rrset(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
+ rrset_func *action, void *action_data)
+{
+ isc_result_t result;
+ dns_dbnode_t *node;
+ dns_rdatasetiter_t *iter;
+
+ node = NULL;
+ result = dns_db_findnode(db, name, false, &node);
+ if (result == ISC_R_NOTFOUND)
+ return (ISC_R_SUCCESS);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ iter = NULL;
+ result = dns_db_allrdatasets(db, node, ver,
+ (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 = (*action)(action_data, &rdataset);
+
+ dns_rdataset_disassociate(&rdataset);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_iterator;
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+
+ cleanup_iterator:
+ dns_rdatasetiter_destroy(&iter);
+
+ cleanup_node:
+ dns_db_detachnode(db, &node);
+
+ return (result);
+}
+
+/*%
+ * For each RR of 'name' in 'ver' of 'db', call 'action'
+ * with the RR and 'action_data' as arguments. If the name
+ * does not exist, do nothing.
+ *
+ * If 'action' returns an error, abort iteration
+ * and return the error.
+ */
+static isc_result_t
+foreach_node_rr(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
+ rr_func *rr_action, void *rr_action_data)
+{
+ foreach_node_rr_ctx_t ctx;
+ ctx.rr_action = rr_action;
+ ctx.rr_action_data = rr_action_data;
+ return (foreach_rrset(db, ver, name,
+ foreach_node_rr_action, &ctx));
+}
+
+
+/*%
+ * For each of the RRs specified by 'db', 'ver', 'name', 'type',
+ * (which can be dns_rdatatype_any to match any type), and 'covers', call
+ * 'action' with the RR and 'action_data' as arguments. If the name
+ * does not exist, or if no RRset of the given type exists at the name,
+ * do nothing.
+ *
+ * If 'action' returns an error, abort iteration and return the error.
+ */
+static isc_result_t
+foreach_rr(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
+ dns_rdatatype_t type, dns_rdatatype_t covers, rr_func *rr_action,
+ void *rr_action_data)
+{
+
+ isc_result_t result;
+ dns_dbnode_t *node;
+ dns_rdataset_t rdataset;
+
+ if (type == dns_rdatatype_any)
+ return (foreach_node_rr(db, ver, name,
+ rr_action, rr_action_data));
+
+ node = NULL;
+ if (type == dns_rdatatype_nsec3 ||
+ (type == dns_rdatatype_rrsig && covers == dns_rdatatype_nsec3))
+ result = dns_db_findnsec3node(db, name, false, &node);
+ else
+ result = dns_db_findnode(db, name, 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, ver, type, covers,
+ (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))
+ {
+ rr_t rr = { 0, DNS_RDATA_INIT };
+ dns_rdataset_current(&rdataset, &rr.rdata);
+ rr.ttl = rdataset.ttl;
+ result = (*rr_action)(rr_action_data, &rr);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_rdataset;
+ }
+ if (result != ISC_R_NOMORE)
+ goto cleanup_rdataset;
+ result = ISC_R_SUCCESS;
+
+ cleanup_rdataset:
+ dns_rdataset_disassociate(&rdataset);
+ cleanup_node:
+ dns_db_detachnode(db, &node);
+
+ return (result);
+}
+
+/**************************************************************************/
+/*
+ * Various tests on the database contents (for prerequisites, etc).
+ */
+
+/*%
+ * Function type for predicate functions that compare a database RR 'db_rr'
+ * against an update RR 'update_rr'.
+ */
+typedef bool rr_predicate(dns_rdata_t *update_rr, dns_rdata_t *db_rr);
+
+/*%
+ * Helper function for rrset_exists().
+ */
+static isc_result_t
+rrset_exists_action(void *data, rr_t *rr) {
+ UNUSED(data);
+ UNUSED(rr);
+ return (ISC_R_EXISTS);
+}
+
+/*%
+ * Utility macro for RR existence checking functions.
+ *
+ * If the variable 'result' has the value ISC_R_EXISTS or
+ * ISC_R_SUCCESS, set *exists to true or false,
+ * respectively, and return success.
+ *
+ * If 'result' has any other value, there was a failure.
+ * Return the failure result code and do not set *exists.
+ *
+ * This would be more readable as "do { if ... } while(0)",
+ * but that form generates tons of warnings on Solaris 2.6.
+ */
+#define RETURN_EXISTENCE_FLAG \
+ return ((result == ISC_R_EXISTS) ? \
+ (*exists = true, ISC_R_SUCCESS) : \
+ ((result == ISC_R_SUCCESS) ? \
+ (*exists = false, ISC_R_SUCCESS) : \
+ result))
+
+/*%
+ * Set '*exists' to true iff an rrset of the given type exists,
+ * to false otherwise.
+ */
+static isc_result_t
+rrset_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
+ dns_rdatatype_t type, dns_rdatatype_t covers,
+ bool *exists)
+{
+ isc_result_t result;
+ result = foreach_rr(db, ver, name, type, covers,
+ rrset_exists_action, NULL);
+ RETURN_EXISTENCE_FLAG;
+}
+
+/*%
+ * Set '*visible' to true if the RRset exists and is part of the
+ * visible zone. Otherwise '*visible' is set to false unless a
+ * error occurs.
+ */
+static isc_result_t
+rrset_visible(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
+ dns_rdatatype_t type, bool *visible)
+{
+ isc_result_t result;
+ dns_fixedname_t fixed;
+
+ dns_fixedname_init(&fixed);
+ result = dns_db_find(db, name, ver, type, DNS_DBFIND_NOWILD,
+ (isc_stdtime_t) 0, NULL,
+ dns_fixedname_name(&fixed), NULL, NULL);
+ switch (result) {
+ case ISC_R_SUCCESS:
+ *visible = true;
+ break;
+ /*
+ * Glue, obscured, deleted or replaced records.
+ */
+ case DNS_R_DELEGATION:
+ case DNS_R_DNAME:
+ case DNS_R_CNAME:
+ case DNS_R_NXDOMAIN:
+ case DNS_R_NXRRSET:
+ case DNS_R_EMPTYNAME:
+ case DNS_R_COVERINGNSEC:
+ *visible = false;
+ result = ISC_R_SUCCESS;
+ break;
+ default:
+ *visible = false; /* silence false compiler warning */
+ break;
+ }
+ return (result);
+}
+
+/*%
+ * Context struct and helper function for name_exists().
+ */
+
+static isc_result_t
+name_exists_action(void *data, dns_rdataset_t *rrset) {
+ UNUSED(data);
+ UNUSED(rrset);
+ return (ISC_R_EXISTS);
+}
+
+/*%
+ * 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 *ver, dns_name_t *name,
+ bool *exists)
+{
+ isc_result_t result;
+ result = foreach_rrset(db, ver, name,
+ name_exists_action, NULL);
+ RETURN_EXISTENCE_FLAG;
+}
+
+/**************************************************************************/
+/*
+ * Checking of "RRset exists (value dependent)" prerequisites.
+ *
+ * In the RFC2136 section 3.2.5, this is the pseudocode involving
+ * a variable called "temp", a mapping of <name, type> tuples to rrsets.
+ *
+ * Here, we represent the "temp" data structure as (non-minimal) "dns_diff_t"
+ * where each tuple has op==DNS_DIFFOP_EXISTS.
+ */
+
+/*%
+ * A comparison function defining the sorting order for the entries
+ * in the "temp" data structure. The major sort key is the owner name,
+ * followed by the type and rdata.
+ */
+static int
+temp_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 = dns_name_compare(&a->name, &b->name);
+ if (r != 0)
+ return (r);
+ r = (b->rdata.type - a->rdata.type);
+ if (r != 0)
+ return (r);
+ r = dns_rdata_casecompare(&a->rdata, &b->rdata);
+ return (r);
+}
+
+/**************************************************************************/
+/*
+ * Conditional deletion of RRs.
+ */
+
+/*%
+ * Context structure for delete_if().
+ */
+
+typedef struct {
+ rr_predicate *predicate;
+ dns_db_t *db;
+ dns_dbversion_t *ver;
+ dns_diff_t *diff;
+ dns_name_t *name;
+ dns_rdata_t *update_rr;
+} conditional_delete_ctx_t;
+
+/*%
+ * Predicate functions for delete_if().
+ */
+
+/*%
+ * Return true always.
+ */
+static bool
+true_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) {
+ UNUSED(update_rr);
+ UNUSED(db_rr);
+ return (true);
+}
+
+/*%
+ * Return true if the record is a RRSIG.
+ */
+static bool
+rrsig_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) {
+ UNUSED(update_rr);
+ return ((db_rr->type == dns_rdatatype_rrsig) ?
+ true : false);
+}
+
+/*%
+ * Internal helper function for delete_if().
+ */
+static isc_result_t
+delete_if_action(void *data, rr_t *rr) {
+ conditional_delete_ctx_t *ctx = data;
+ if ((*ctx->predicate)(ctx->update_rr, &rr->rdata)) {
+ isc_result_t result;
+ result = update_one_rr(ctx->db, ctx->ver, ctx->diff,
+ DNS_DIFFOP_DEL, ctx->name,
+ rr->ttl, &rr->rdata);
+ return (result);
+ } else {
+ return (ISC_R_SUCCESS);
+ }
+}
+
+/*%
+ * Conditionally delete RRs. Apply 'predicate' to the RRs
+ * specified by 'db', 'ver', 'name', and 'type' (which can
+ * be dns_rdatatype_any to match any type). Delete those
+ * RRs for which the predicate returns true, and log the
+ * deletions in 'diff'.
+ */
+static isc_result_t
+delete_if(rr_predicate *predicate, dns_db_t *db, dns_dbversion_t *ver,
+ dns_name_t *name, dns_rdatatype_t type, dns_rdatatype_t covers,
+ dns_rdata_t *update_rr, dns_diff_t *diff)
+{
+ conditional_delete_ctx_t ctx;
+ ctx.predicate = predicate;
+ ctx.db = db;
+ ctx.ver = ver;
+ ctx.diff = diff;
+ ctx.name = name;
+ ctx.update_rr = update_rr;
+ return (foreach_rr(db, ver, name, type, covers,
+ delete_if_action, &ctx));
+}
+
+/**************************************************************************/
+/*
+ * Incremental updating of NSECs and RRSIGs.
+ */
+
+/*%
+ * We abuse the dns_diff_t type to represent a set of domain names
+ * affected by the update.
+ */
+static isc_result_t
+namelist_append_name(dns_diff_t *list, dns_name_t *name) {
+ isc_result_t result;
+ dns_difftuple_t *tuple = NULL;
+ static dns_rdata_t dummy_rdata = DNS_RDATA_INIT;
+
+ CHECK(dns_difftuple_create(list->mctx, DNS_DIFFOP_EXISTS, name, 0,
+ &dummy_rdata, &tuple));
+ dns_diff_append(list, &tuple);
+ failure:
+ return (result);
+}
+
+static isc_result_t
+namelist_append_subdomain(dns_db_t *db, dns_name_t *name, dns_diff_t *affected)
+{
+ isc_result_t result;
+ dns_fixedname_t fixedname;
+ dns_name_t *child;
+ dns_dbiterator_t *dbit = NULL;
+
+ child = dns_fixedname_initname(&fixedname);
+
+ CHECK(dns_db_createiterator(db, DNS_DB_NONSEC3, &dbit));
+
+ for (result = dns_dbiterator_seek(dbit, name);
+ result == ISC_R_SUCCESS;
+ result = dns_dbiterator_next(dbit))
+ {
+ dns_dbnode_t *node = NULL;
+ CHECK(dns_dbiterator_current(dbit, &node, child));
+ dns_db_detachnode(db, &node);
+ if (! dns_name_issubdomain(child, name))
+ break;
+ CHECK(namelist_append_name(affected, child));
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ failure:
+ if (dbit != NULL)
+ dns_dbiterator_destroy(&dbit);
+ return (result);
+}
+
+
+
+/*%
+ * Helper function for non_nsec_rrset_exists().
+ */
+static isc_result_t
+is_non_nsec_action(void *data, dns_rdataset_t *rrset) {
+ UNUSED(data);
+ if (!(rrset->type == dns_rdatatype_nsec ||
+ rrset->type == dns_rdatatype_nsec3 ||
+ (rrset->type == dns_rdatatype_rrsig &&
+ (rrset->covers == dns_rdatatype_nsec ||
+ rrset->covers == dns_rdatatype_nsec3))))
+ return (ISC_R_EXISTS);
+ return (ISC_R_SUCCESS);
+}
+
+/*%
+ * Check whether there is an rrset other than a NSEC or RRSIG NSEC,
+ * i.e., anything that justifies the continued existence of a name
+ * after a secure update.
+ *
+ * If such an rrset exists, set '*exists' to true.
+ * Otherwise, set it to false.
+ */
+static isc_result_t
+non_nsec_rrset_exists(dns_db_t *db, dns_dbversion_t *ver,
+ dns_name_t *name, bool *exists)
+{
+ isc_result_t result;
+ result = foreach_rrset(db, ver, name, is_non_nsec_action, NULL);
+ RETURN_EXISTENCE_FLAG;
+}
+
+/*%
+ * A comparison function for sorting dns_diff_t:s by name.
+ */
+static int
+name_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;
+ return (dns_name_compare(&a->name, &b->name));
+}
+
+static isc_result_t
+uniqify_name_list(dns_diff_t *list) {
+ isc_result_t result;
+ dns_difftuple_t *p, *q;
+
+ CHECK(dns_diff_sort(list, name_order));
+
+ p = ISC_LIST_HEAD(list->tuples);
+ while (p != NULL) {
+ do {
+ q = ISC_LIST_NEXT(p, link);
+ if (q == NULL || ! dns_name_equal(&p->name, &q->name))
+ break;
+ ISC_LIST_UNLINK(list->tuples, q, link);
+ dns_difftuple_free(&q);
+ } while (1);
+ p = ISC_LIST_NEXT(p, link);
+ }
+ failure:
+ return (result);
+}
+
+static isc_result_t
+is_active(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
+ bool *flag, bool *cut, bool *unsecure)
+{
+ isc_result_t result;
+ dns_fixedname_t foundname;
+ dns_fixedname_init(&foundname);
+ result = dns_db_find(db, name, ver, dns_rdatatype_any,
+ DNS_DBFIND_GLUEOK | DNS_DBFIND_NOWILD,
+ (isc_stdtime_t) 0, NULL,
+ dns_fixedname_name(&foundname),
+ NULL, NULL);
+ if (result == ISC_R_SUCCESS || result == DNS_R_EMPTYNAME) {
+ *flag = true;
+ *cut = false;
+ if (unsecure != NULL)
+ *unsecure = false;
+ return (ISC_R_SUCCESS);
+ } else if (result == DNS_R_ZONECUT) {
+ *flag = true;
+ *cut = true;
+ if (unsecure != NULL) {
+ /*
+ * We are at the zonecut. Check to see if there
+ * is a DS RRset.
+ */
+ if (dns_db_find(db, name, ver, dns_rdatatype_ds, 0,
+ (isc_stdtime_t) 0, NULL,
+ dns_fixedname_name(&foundname),
+ NULL, NULL) == DNS_R_NXRRSET)
+ *unsecure = true;
+ else
+ *unsecure = false;
+ }
+ return (ISC_R_SUCCESS);
+ } else if (result == DNS_R_GLUE || result == DNS_R_DNAME ||
+ result == DNS_R_DELEGATION || result == DNS_R_NXDOMAIN) {
+ *flag = false;
+ *cut = false;
+ if (unsecure != NULL)
+ *unsecure = false;
+ return (ISC_R_SUCCESS);
+ } else {
+ /*
+ * Silence compiler.
+ */
+ *flag = false;
+ *cut = false;
+ if (unsecure != NULL)
+ *unsecure = false;
+ return (result);
+ }
+}
+
+/*%
+ * Find the next/previous name that has a NSEC record.
+ * In other words, skip empty database nodes and names that
+ * have had their NSECs removed because they are obscured by
+ * a zone cut.
+ */
+static isc_result_t
+next_active(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db,
+ dns_dbversion_t *ver, dns_name_t *oldname, dns_name_t *newname,
+ bool forward)
+{
+ isc_result_t result;
+ dns_dbiterator_t *dbit = NULL;
+ bool has_nsec = false;
+ unsigned int wraps = 0;
+ bool secure = dns_db_issecure(db);
+
+ CHECK(dns_db_createiterator(db, 0, &dbit));
+
+ CHECK(dns_dbiterator_seek(dbit, oldname));
+ do {
+ dns_dbnode_t *node = NULL;
+
+ if (forward)
+ result = dns_dbiterator_next(dbit);
+ else
+ result = dns_dbiterator_prev(dbit);
+ if (result == ISC_R_NOMORE) {
+ /*
+ * Wrap around.
+ */
+ if (forward)
+ CHECK(dns_dbiterator_first(dbit));
+ else
+ CHECK(dns_dbiterator_last(dbit));
+ wraps++;
+ if (wraps == 2) {
+ update_log(log, zone, ISC_LOG_ERROR,
+ "secure zone with no NSECs");
+ result = DNS_R_BADZONE;
+ goto failure;
+ }
+ }
+ CHECK(dns_dbiterator_current(dbit, &node, newname));
+ dns_db_detachnode(db, &node);
+
+ /*
+ * The iterator may hold the tree lock, and
+ * rrset_exists() calls dns_db_findnode() which
+ * may try to reacquire it. To avoid deadlock
+ * we must pause the iterator first.
+ */
+ CHECK(dns_dbiterator_pause(dbit));
+ if (secure) {
+ CHECK(rrset_exists(db, ver, newname,
+ dns_rdatatype_nsec, 0, &has_nsec));
+ } else {
+ dns_fixedname_t ffound;
+ dns_name_t *found;
+ found = dns_fixedname_initname(&ffound);
+ result = dns_db_find(db, newname, ver,
+ dns_rdatatype_soa,
+ DNS_DBFIND_NOWILD, 0, NULL, found,
+ NULL, NULL);
+ if (result == ISC_R_SUCCESS ||
+ result == DNS_R_EMPTYNAME ||
+ result == DNS_R_NXRRSET ||
+ result == DNS_R_CNAME ||
+ (result == DNS_R_DELEGATION &&
+ dns_name_equal(newname, found))) {
+ has_nsec = true;
+ result = ISC_R_SUCCESS;
+ } else if (result != DNS_R_NXDOMAIN)
+ break;
+ }
+ } while (! has_nsec);
+ failure:
+ if (dbit != NULL)
+ dns_dbiterator_destroy(&dbit);
+
+ return (result);
+}
+
+/*%
+ * Add a NSEC record for "name", recording the change in "diff".
+ * The existing NSEC is removed.
+ */
+static isc_result_t
+add_nsec(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db,
+ dns_dbversion_t *ver, dns_name_t *name, dns_ttl_t nsecttl,
+ dns_diff_t *diff)
+{
+ isc_result_t result;
+ dns_dbnode_t *node = NULL;
+ unsigned char buffer[DNS_NSEC_BUFFERSIZE];
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_difftuple_t *tuple = NULL;
+ dns_fixedname_t fixedname;
+ dns_name_t *target;
+
+ target = dns_fixedname_initname(&fixedname);
+
+ /*
+ * Find the successor name, aka NSEC target.
+ */
+ CHECK(next_active(log, zone, db, ver, name, target, true));
+
+ /*
+ * Create the NSEC RDATA.
+ */
+ CHECK(dns_db_findnode(db, name, false, &node));
+ dns_rdata_init(&rdata);
+ CHECK(dns_nsec_buildrdata(db, ver, node, target, buffer, &rdata));
+ dns_db_detachnode(db, &node);
+
+ /*
+ * Delete the old NSEC and record the change.
+ */
+ CHECK(delete_if(true_p, db, ver, name, dns_rdatatype_nsec, 0,
+ NULL, diff));
+ /*
+ * Add the new NSEC and record the change.
+ */
+ CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, name,
+ nsecttl, &rdata, &tuple));
+ CHECK(do_one_tuple(&tuple, db, ver, diff));
+ INSIST(tuple == NULL);
+
+ failure:
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ return (result);
+}
+
+/*%
+ * Add a placeholder NSEC record for "name", recording the change in "diff".
+ */
+static isc_result_t
+add_placeholder_nsec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
+ dns_diff_t *diff)
+{
+ isc_result_t result;
+ dns_difftuple_t *tuple = NULL;
+ isc_region_t r;
+ unsigned char data[1] = { 0 }; /* The root domain, no bits. */
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+
+ r.base = data;
+ r.length = sizeof(data);
+ dns_rdata_fromregion(&rdata, dns_db_class(db), dns_rdatatype_nsec, &r);
+ CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, name, 0,
+ &rdata, &tuple));
+ CHECK(do_one_tuple(&tuple, db, ver, diff));
+ failure:
+ return (result);
+}
+
+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), false, &node));
+ CHECK(dns_dnssec_findzonekeys2(db, ver, node, dns_db_origin(db),
+ directory, mctx, maxkeys, keys, nkeys));
+ failure:
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ return (result);
+}
+
+/*%
+ * Add RRSIG records for an RRset, recording the change in "diff".
+ */
+static isc_result_t
+add_sigs(dns_update_log_t *log, 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 inception, isc_stdtime_t expire,
+ bool check_ksk, bool keyset_kskonly)
+{
+ isc_result_t result;
+ dns_dbnode_t *node = NULL;
+ dns_rdataset_t rdataset;
+ dns_rdata_t sig_rdata = DNS_RDATA_INIT;
+ isc_buffer_t buffer;
+ unsigned char data[1024]; /* XXX */
+ unsigned int i, j;
+ bool added_sig = false;
+ isc_mem_t *mctx = diff->mctx;
+
+ dns_rdataset_init(&rdataset);
+ isc_buffer_init(&buffer, data, sizeof(data));
+
+ /* Get the rdataset to sign. */
+ if (type == dns_rdatatype_nsec3)
+ CHECK(dns_db_findnsec3node(db, name, false, &node));
+ else
+ CHECK(dns_db_findnode(db, name, false, &node));
+ CHECK(dns_db_findrdataset(db, node, ver, type, 0,
+ (isc_stdtime_t) 0, &rdataset, NULL));
+ dns_db_detachnode(db, &node);
+
+#define REVOKE(x) ((dst_key_flags(x) & DNS_KEYFLAG_REVOKE) != 0)
+#define KSK(x) ((dst_key_flags(x) & DNS_KEYFLAG_KSK) != 0)
+#define ALG(x) dst_key_alg(x)
+
+ /*
+ * If we are honoring KSK flags then we need to check that we
+ * have both KSK and non-KSK keys that are not revoked per
+ * algorithm.
+ */
+ for (i = 0; i < nkeys; i++) {
+ bool both = false;
+
+ if (!dst_key_isprivate(keys[i]))
+ continue;
+ if (dst_key_inactive(keys[i])) /* Should be redundant. */
+ continue;
+
+ if (check_ksk && !REVOKE(keys[i])) {
+ bool have_ksk, have_nonksk;
+ if (KSK(keys[i])) {
+ have_ksk = true;
+ have_nonksk = false;
+ } else {
+ have_ksk = false;
+ have_nonksk = true;
+ }
+ for (j = 0; j < nkeys; j++) {
+ if (j == i || ALG(keys[i]) != ALG(keys[j]))
+ continue;
+ if (!dst_key_isprivate(keys[j]))
+ continue;
+ if (dst_key_inactive(keys[j])) /* SBR */
+ continue;
+ if (REVOKE(keys[j]))
+ continue;
+ if (KSK(keys[j]))
+ have_ksk = true;
+ else
+ have_nonksk = true;
+ both = have_ksk && have_nonksk;
+ if (both)
+ break;
+ }
+ }
+
+ if (both) {
+ if (type == dns_rdatatype_dnskey) {
+ if (!KSK(keys[i]) && keyset_kskonly)
+ continue;
+ } else if (KSK(keys[i])) {
+ /*
+ * CDS and CDNSKEY are signed with KSK
+ * (RFC 7344, 4.1).
+ */
+ if (type != dns_rdatatype_cds &&
+ type != dns_rdatatype_cdnskey)
+ continue;
+ }
+ } else if (REVOKE(keys[i]) && type != dns_rdatatype_dnskey)
+ 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);
+ isc_buffer_init(&buffer, data, sizeof(data));
+ added_sig = true;
+ }
+ if (!added_sig) {
+ update_log(log, zone, ISC_LOG_ERROR,
+ "found no active private keys, "
+ "unable to generate any signatures");
+ result = ISC_R_NOTFOUND;
+ }
+
+ failure:
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ return (result);
+}
+
+/*
+ * Delete expired RRsigs and any RRsigs we are about to re-sign.
+ * See also zone.c:del_sigs().
+ */
+static isc_result_t
+del_keysigs(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
+ dns_diff_t *diff, dst_key_t **keys, unsigned int nkeys)
+{
+ 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;
+ bool found;
+
+ dns_rdataset_init(&rdataset);
+
+ result = dns_db_findnode(db, name, 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,
+ dns_rdatatype_dnskey, (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);
+ found = false;
+ for (i = 0; i < nkeys; i++) {
+ if (rrsig.keyid == dst_key_id(keys[i])) {
+ found = true;
+ if (!dst_key_isprivate(keys[i]) &&
+ !dst_key_inactive(keys[i]))
+ {
+ /*
+ * The re-signing code in zone.c
+ * will mark this as offline.
+ * Just skip the record for now.
+ */
+ 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;
+failure:
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ return (result);
+}
+
+static isc_result_t
+add_exposed_sigs(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db,
+ dns_dbversion_t *ver, dns_name_t *name, bool cut,
+ dns_diff_t *diff, dst_key_t **keys, unsigned int nkeys,
+ isc_stdtime_t inception, isc_stdtime_t expire,
+ bool check_ksk, bool keyset_kskonly,
+ unsigned int *sigs)
+{
+ isc_result_t result;
+ dns_dbnode_t *node;
+ dns_rdatasetiter_t *iter;
+
+ node = NULL;
+ result = dns_db_findnode(db, name, false, &node);
+ if (result == ISC_R_NOTFOUND)
+ return (ISC_R_SUCCESS);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ iter = NULL;
+ result = dns_db_allrdatasets(db, node, ver,
+ (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_rdatatype_t type;
+ bool flag;
+
+ dns_rdataset_init(&rdataset);
+ dns_rdatasetiter_current(iter, &rdataset);
+ type = rdataset.type;
+ dns_rdataset_disassociate(&rdataset);
+
+ /*
+ * We don't need to sign unsigned NSEC records at the cut
+ * as they are handled elsewhere.
+ */
+ if ((type == dns_rdatatype_rrsig) ||
+ (cut && type != dns_rdatatype_ds))
+ continue;
+ result = rrset_exists(db, ver, name, dns_rdatatype_rrsig,
+ type, &flag);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_iterator;
+ if (flag)
+ continue;;
+ result = add_sigs(log, zone, db, ver, name, type, diff,
+ keys, nkeys, inception, expire,
+ check_ksk, keyset_kskonly);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_iterator;
+ (*sigs)++;
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+
+ cleanup_iterator:
+ dns_rdatasetiter_destroy(&iter);
+
+ cleanup_node:
+ dns_db_detachnode(db, &node);
+
+ return (result);
+}
+
+/*%
+ * Update RRSIG, NSEC and NSEC3 records affected by an update. The original
+ * update, including the SOA serial update but excluding the RRSIG & NSEC
+ * changes, is in "diff" and has already been applied to "newver" of "db".
+ * The database version prior to the update is "oldver".
+ *
+ * The necessary RRSIG, NSEC and NSEC3 changes will be applied to "newver"
+ * and added (as a minimal diff) to "diff".
+ *
+ * The RRSIGs generated will be valid for 'sigvalidityinterval' seconds.
+ */
+isc_result_t
+dns_update_signatures(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db,
+ dns_dbversion_t *oldver, dns_dbversion_t *newver,
+ dns_diff_t *diff, uint32_t sigvalidityinterval)
+{
+ return (dns_update_signaturesinc(log, zone, db, oldver, newver, diff,
+ sigvalidityinterval, NULL));
+}
+
+struct dns_update_state {
+ unsigned int magic;
+ dns_diff_t diffnames;
+ dns_diff_t affected;
+ dns_diff_t sig_diff;
+ dns_diff_t nsec_diff;
+ dns_diff_t nsec_mindiff;
+ dns_diff_t work;
+ dst_key_t *zone_keys[DNS_MAXZONEKEYS];
+ unsigned int nkeys;
+ isc_stdtime_t inception, expire;
+ dns_ttl_t nsecttl;
+ bool check_ksk, keyset_kskonly, build_nsec3;
+ enum { sign_updates, remove_orphaned, build_chain, process_nsec,
+ sign_nsec, update_nsec3, process_nsec3, sign_nsec3 } state;
+};
+
+isc_result_t
+dns_update_signaturesinc(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db,
+ dns_dbversion_t *oldver, dns_dbversion_t *newver,
+ dns_diff_t *diff, uint32_t sigvalidityinterval,
+ dns_update_state_t **statep)
+{
+ isc_result_t result = ISC_R_SUCCESS;
+ dns_update_state_t mystate, *state;
+
+ dns_difftuple_t *t, *next;
+ bool flag, build_nsec;
+ unsigned int i;
+ isc_stdtime_t now;
+ dns_rdata_soa_t soa;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdataset_t rdataset;
+ dns_dbnode_t *node = NULL;
+ bool unsecure;
+ bool cut;
+ dns_rdatatype_t privatetype = dns_zone_getprivatetype(zone);
+ unsigned int sigs = 0;
+ unsigned int maxsigs = dns_zone_getsignatures(zone);
+
+ if (statep == NULL || *statep == NULL) {
+ if (statep == NULL) {
+ state = &mystate;
+ } else {
+ state = isc_mem_get(diff->mctx, sizeof(*state));
+ if (state == NULL)
+ return (ISC_R_NOMEMORY);
+ }
+
+ dns_diff_init(diff->mctx, &state->diffnames);
+ dns_diff_init(diff->mctx, &state->affected);
+ dns_diff_init(diff->mctx, &state->sig_diff);
+ dns_diff_init(diff->mctx, &state->nsec_diff);
+ dns_diff_init(diff->mctx, &state->nsec_mindiff);
+ dns_diff_init(diff->mctx, &state->work);
+ state->nkeys = 0;
+ state->build_nsec3 = false;
+
+ result = find_zone_keys(zone, db, newver, diff->mctx,
+ DNS_MAXZONEKEYS, state->zone_keys,
+ &state->nkeys);
+ if (result != ISC_R_SUCCESS) {
+ update_log(log, zone, ISC_LOG_ERROR,
+ "could not get zone keys for secure "
+ "dynamic update");
+ goto failure;
+ }
+
+ isc_stdtime_get(&now);
+ state->inception = now - 3600; /* Allow for some clock skew. */
+ state->expire = now + sigvalidityinterval;
+
+ /*
+ * Do we look at the KSK flag on the DNSKEY to determining which
+ * keys sign which RRsets? First check the zone option then
+ * check the keys flags to make sure at least one has a ksk set
+ * and one doesn't.
+ */
+ state->check_ksk =
+ (dns_zone_getoptions(zone) & DNS_ZONEOPT_UPDATECHECKKSK);
+ state->keyset_kskonly =
+ (dns_zone_getoptions(zone) & DNS_ZONEOPT_DNSKEYKSKONLY);
+
+ /*
+ * Get the NSEC/NSEC3 TTL from the SOA MINIMUM field.
+ */
+ CHECK(dns_db_findnode(db, dns_db_origin(db), false, &node));
+ dns_rdataset_init(&rdataset);
+ CHECK(dns_db_findrdataset(db, node, newver, dns_rdatatype_soa,
+ 0, (isc_stdtime_t) 0, &rdataset,
+ NULL));
+ CHECK(dns_rdataset_first(&rdataset));
+ dns_rdataset_current(&rdataset, &rdata);
+ CHECK(dns_rdata_tostruct(&rdata, &soa, NULL));
+ state->nsecttl = soa.minimum;
+ dns_rdataset_disassociate(&rdataset);
+ dns_db_detachnode(db, &node);
+
+ /*
+ * Find all RRsets directly affected by the update, and
+ * update their RRSIGs. Also build a list of names affected
+ * by the update in "diffnames".
+ */
+ CHECK(dns_diff_sort(diff, temp_order));
+ state->state = sign_updates;
+ state->magic = STATE_MAGIC;
+ if (statep != NULL)
+ *statep = state;
+ } else {
+ REQUIRE(DNS_STATE_VALID(*statep));
+ state = *statep;
+ }
+
+ next_state:
+ switch (state->state) {
+ case sign_updates:
+ t = ISC_LIST_HEAD(diff->tuples);
+ while (t != NULL) {
+ dns_name_t *name = &t->name;
+ /*
+ * Now "name" is a new, unique name affected by the
+ * update.
+ */
+
+ CHECK(namelist_append_name(&state->diffnames, name));
+
+ while (t != NULL && dns_name_equal(&t->name, name)) {
+ dns_rdatatype_t type;
+ type = t->rdata.type;
+
+ /*
+ * Now "name" and "type" denote a new unique
+ * RRset affected by the update.
+ */
+
+ /* Don't sign RRSIGs. */
+ if (type == dns_rdatatype_rrsig)
+ goto skip;
+
+ /*
+ * Delete all old RRSIGs covering this type,
+ * since they are all invalid when the signed
+ * RRset has changed. We may not be able to
+ * recreate all of them - tough.
+ * Special case changes to the zone's DNSKEY
+ * records to support offline KSKs.
+ */
+ if (type == dns_rdatatype_dnskey)
+ del_keysigs(db, newver, name,
+ &state->sig_diff,
+ state->zone_keys,
+ state->nkeys);
+ else
+ CHECK(delete_if(true_p, db, newver,
+ name,
+ dns_rdatatype_rrsig,
+ type, NULL,
+ &state->sig_diff));
+
+ /*
+ * If this RRset is still visible after the
+ * update, add a new signature for it.
+ */
+ CHECK(rrset_visible(db, newver, name, type,
+ &flag));
+ if (flag) {
+ CHECK(add_sigs(log, zone, db, newver,
+ name, type,
+ &state->sig_diff,
+ state->zone_keys,
+ state->nkeys,
+ state->inception,
+ state->expire,
+ state->check_ksk,
+ state->keyset_kskonly));
+ sigs++;
+ }
+ skip:
+ /* Skip any other updates to the same RRset. */
+ while (t != NULL &&
+ dns_name_equal(&t->name, name) &&
+ t->rdata.type == type)
+ {
+ next = ISC_LIST_NEXT(t, link);
+ ISC_LIST_UNLINK(diff->tuples, t, link);
+ ISC_LIST_APPEND(state->work.tuples, t,
+ link);
+ t = next;
+ }
+ }
+ if (state != &mystate && sigs > maxsigs)
+ return (DNS_R_CONTINUE);
+ }
+ ISC_LIST_APPENDLIST(diff->tuples, state->work.tuples, link);
+
+ update_log(log, zone, ISC_LOG_DEBUG(3),
+ "updated data signatures");
+ /* FALLTHROUGH */
+ case remove_orphaned:
+ state->state = remove_orphaned;
+
+ /* Remove orphaned NSECs and RRSIG NSECs. */
+ for (t = ISC_LIST_HEAD(state->diffnames.tuples);
+ t != NULL;
+ t = ISC_LIST_NEXT(t, link))
+ {
+ CHECK(non_nsec_rrset_exists(db, newver,
+ &t->name, &flag));
+ if (!flag) {
+ CHECK(delete_if(true_p, db, newver, &t->name,
+ dns_rdatatype_any, 0,
+ NULL, &state->sig_diff));
+ }
+ }
+ update_log(log, zone, ISC_LOG_DEBUG(3),
+ "removed any orphaned NSEC records");
+
+ /*
+ * See if we need to build NSEC or NSEC3 chains.
+ */
+ CHECK(dns_private_chains(db, newver, privatetype, &build_nsec,
+ &state->build_nsec3));
+ if (!build_nsec) {
+ state->state = update_nsec3;
+ goto next_state;
+ }
+
+ update_log(log, zone, ISC_LOG_DEBUG(3),
+ "rebuilding NSEC chain");
+
+ /* FALLTHROUGH */
+ case build_chain:
+ state->state = build_chain;
+ /*
+ * When a name is created or deleted, its predecessor needs to
+ * have its NSEC updated.
+ */
+ for (t = ISC_LIST_HEAD(state->diffnames.tuples);
+ t != NULL;
+ t = ISC_LIST_NEXT(t, link))
+ {
+ bool existed, exists;
+ dns_fixedname_t fixedname;
+ dns_name_t *prevname;
+
+ prevname = dns_fixedname_initname(&fixedname);
+
+ if (oldver != NULL)
+ CHECK(name_exists(db, oldver, &t->name,
+ &existed));
+ else
+ existed = false;
+ CHECK(name_exists(db, newver, &t->name, &exists));
+ if (exists == existed)
+ continue;
+
+ /*
+ * Find the predecessor.
+ * When names become obscured or unobscured in this
+ * update transaction, we may find the wrong
+ * predecessor because the NSECs have not yet been
+ * updated to reflect the delegation change. This
+ * should not matter because in this case, the correct
+ * predecessor is either the delegation node or a
+ * newly unobscured node, and those nodes are on the
+ * "affected" list in any case.
+ */
+ CHECK(next_active(log, zone, db, newver,
+ &t->name, prevname, false));
+ CHECK(namelist_append_name(&state->affected, prevname));
+ }
+
+ /*
+ * Find names potentially affected by delegation changes
+ * (obscured by adding an NS or DNAME, or unobscured by
+ * removing one).
+ */
+ for (t = ISC_LIST_HEAD(state->diffnames.tuples);
+ t != NULL;
+ t = ISC_LIST_NEXT(t, link))
+ {
+ bool ns_existed, dname_existed;
+ bool ns_exists, dname_exists;
+
+ if (oldver != NULL)
+ CHECK(rrset_exists(db, oldver, &t->name,
+ dns_rdatatype_ns, 0,
+ &ns_existed));
+ else
+ ns_existed = false;
+ if (oldver != NULL)
+ CHECK(rrset_exists(db, oldver, &t->name,
+ dns_rdatatype_dname, 0,
+ &dname_existed));
+ else
+ dname_existed = false;
+ CHECK(rrset_exists(db, newver, &t->name,
+ dns_rdatatype_ns, 0, &ns_exists));
+ CHECK(rrset_exists(db, newver, &t->name,
+ dns_rdatatype_dname, 0,
+ &dname_exists));
+ if ((ns_exists || dname_exists) ==
+ (ns_existed || dname_existed))
+ continue;
+ /*
+ * There was a delegation change. Mark all subdomains
+ * of t->name as potentially needing a NSEC update.
+ */
+ CHECK(namelist_append_subdomain(db, &t->name,
+ &state->affected));
+ }
+ ISC_LIST_APPENDLIST(state->affected.tuples,
+ state->diffnames.tuples, link);
+ INSIST(ISC_LIST_EMPTY(state->diffnames.tuples));
+
+ CHECK(uniqify_name_list(&state->affected));
+
+ /* FALLTHROUGH */
+ case process_nsec:
+ state->state = process_nsec;
+
+ /*
+ * Determine which names should have NSECs, and delete/create
+ * NSECs to make it so. We don't know the final NSEC targets
+ * yet, so we just create placeholder NSECs with arbitrary
+ * contents to indicate that their respective owner names
+ * should be part of the NSEC chain.
+ */
+ while ((t = ISC_LIST_HEAD(state->affected.tuples)) != NULL) {
+ bool exists;
+ dns_name_t *name = &t->name;
+
+ CHECK(name_exists(db, newver, name, &exists));
+ if (! exists)
+ goto unlink;
+ CHECK(is_active(db, newver, name, &flag, &cut, NULL));
+ if (!flag) {
+ /*
+ * This name is obscured. Delete any
+ * existing NSEC record.
+ */
+ CHECK(delete_if(true_p, db, newver, name,
+ dns_rdatatype_nsec, 0,
+ NULL, &state->nsec_diff));
+ CHECK(delete_if(rrsig_p, db, newver, name,
+ dns_rdatatype_any, 0, NULL,
+ diff));
+ } else {
+ /*
+ * This name is not obscured. It needs to have
+ * a NSEC unless it is the at the origin, in
+ * which case it should already exist if there
+ * is a complete NSEC chain and if there isn't
+ * a complete NSEC chain we don't want to add
+ * one as that would signal that there is a
+ * complete NSEC chain.
+ */
+ if (!dns_name_equal(name, dns_db_origin(db))) {
+ CHECK(rrset_exists(db, newver, name,
+ dns_rdatatype_nsec,
+ 0, &flag));
+ if (!flag)
+ CHECK(add_placeholder_nsec(db,
+ newver, name, diff));
+ }
+ CHECK(add_exposed_sigs(log, zone, db, newver,
+ name, cut,
+ &state->sig_diff,
+ state->zone_keys,
+ state->nkeys,
+ state->inception,
+ state->expire,
+ state->check_ksk,
+ state->keyset_kskonly,
+ &sigs));
+ }
+ unlink:
+ ISC_LIST_UNLINK(state->affected.tuples, t, link);
+ ISC_LIST_APPEND(state->work.tuples, t, link);
+ if (state != &mystate && sigs > maxsigs)
+ return (DNS_R_CONTINUE);
+ }
+ ISC_LIST_APPENDLIST(state->affected.tuples,
+ state->work.tuples, link);
+
+ /*
+ * Now we know which names are part of the NSEC chain.
+ * Make them all point at their correct targets.
+ */
+ for (t = ISC_LIST_HEAD(state->affected.tuples);
+ t != NULL;
+ t = ISC_LIST_NEXT(t, link))
+ {
+ CHECK(rrset_exists(db, newver, &t->name,
+ dns_rdatatype_nsec, 0, &flag));
+ if (flag) {
+ /*
+ * There is a NSEC, but we don't know if it
+ * is correct. Delete it and create a correct
+ * one to be sure. If the update was
+ * unnecessary, the diff minimization
+ * will take care of eliminating it from the
+ * journal, IXFRs, etc.
+ *
+ * The RRSIG bit should always be set in the
+ * NSECs we generate, because they will all
+ * get RRSIG NSECs.
+ * (XXX what if the zone keys are missing?).
+ * Because the RRSIG NSECs have not necessarily
+ * been created yet, the correctness of the
+ * bit mask relies on the assumption that NSECs
+ * are only created if there is other data, and
+ * if there is other data, there are other
+ * RRSIGs.
+ */
+ CHECK(add_nsec(log, zone, db, newver, &t->name,
+ state->nsecttl,
+ &state->nsec_diff));
+ }
+ }
+
+ /*
+ * Minimize the set of NSEC updates so that we don't
+ * have to regenerate the RRSIG NSECs for NSECs that were
+ * replaced with identical ones.
+ */
+ while ((t = ISC_LIST_HEAD(state->nsec_diff.tuples)) != NULL) {
+ ISC_LIST_UNLINK(state->nsec_diff.tuples, t, link);
+ dns_diff_appendminimal(&state->nsec_mindiff, &t);
+ }
+
+ update_log(log, zone, ISC_LOG_DEBUG(3),
+ "signing rebuilt NSEC chain");
+
+ /* FALLTHROUGH */
+ case sign_nsec:
+ state->state = sign_nsec;
+ /* Update RRSIG NSECs. */
+ while ((t = ISC_LIST_HEAD(state->nsec_mindiff.tuples)) != NULL)
+ {
+ if (t->op == DNS_DIFFOP_DEL) {
+ CHECK(delete_if(true_p, db, newver, &t->name,
+ dns_rdatatype_rrsig,
+ dns_rdatatype_nsec,
+ NULL, &state->sig_diff));
+ } else if (t->op == DNS_DIFFOP_ADD) {
+ CHECK(add_sigs(log, zone, db, newver, &t->name,
+ dns_rdatatype_nsec,
+ &state->sig_diff,
+ state->zone_keys, state->nkeys,
+ state->inception, state->expire,
+ state->check_ksk,
+ state->keyset_kskonly));
+ sigs++;
+ } else {
+ INSIST(0);
+ }
+ ISC_LIST_UNLINK(state->nsec_mindiff.tuples, t, link);
+ ISC_LIST_APPEND(state->work.tuples, t, link);
+ if (state != &mystate && sigs > maxsigs)
+ return (DNS_R_CONTINUE);
+ }
+ ISC_LIST_APPENDLIST(state->nsec_mindiff.tuples,
+ state->work.tuples, link);
+ /* FALLTHROUGH */
+ case update_nsec3:
+ state->state = update_nsec3;
+
+ /* Record our changes for the journal. */
+ while ((t = ISC_LIST_HEAD(state->sig_diff.tuples)) != NULL) {
+ ISC_LIST_UNLINK(state->sig_diff.tuples, t, link);
+ dns_diff_appendminimal(diff, &t);
+ }
+ while ((t = ISC_LIST_HEAD(state->nsec_mindiff.tuples)) != NULL)
+ {
+ ISC_LIST_UNLINK(state->nsec_mindiff.tuples, t, link);
+ dns_diff_appendminimal(diff, &t);
+ }
+
+ INSIST(ISC_LIST_EMPTY(state->sig_diff.tuples));
+ INSIST(ISC_LIST_EMPTY(state->nsec_diff.tuples));
+ INSIST(ISC_LIST_EMPTY(state->nsec_mindiff.tuples));
+
+ if (!state->build_nsec3) {
+ update_log(log, zone, ISC_LOG_DEBUG(3),
+ "no NSEC3 chains to rebuild");
+ goto failure;
+ }
+
+ update_log(log, zone, ISC_LOG_DEBUG(3),
+ "rebuilding NSEC3 chains");
+
+ dns_diff_clear(&state->diffnames);
+ dns_diff_clear(&state->affected);
+
+ CHECK(dns_diff_sort(diff, temp_order));
+
+ /*
+ * Find names potentially affected by delegation changes
+ * (obscured by adding an NS or DNAME, or unobscured by
+ * removing one).
+ */
+ t = ISC_LIST_HEAD(diff->tuples);
+ while (t != NULL) {
+ dns_name_t *name = &t->name;
+
+ bool ns_existed, dname_existed;
+ bool ns_exists, dname_exists;
+ bool exists, existed;
+
+ if (t->rdata.type == dns_rdatatype_nsec ||
+ t->rdata.type == dns_rdatatype_rrsig) {
+ t = ISC_LIST_NEXT(t, link);
+ continue;
+ }
+
+ CHECK(namelist_append_name(&state->affected, name));
+
+ if (oldver != NULL)
+ CHECK(rrset_exists(db, oldver, name,
+ dns_rdatatype_ns,
+ 0, &ns_existed));
+ else
+ ns_existed = false;
+ if (oldver != NULL)
+ CHECK(rrset_exists(db, oldver, name,
+ dns_rdatatype_dname, 0,
+ &dname_existed));
+ else
+ dname_existed = false;
+ CHECK(rrset_exists(db, newver, name, dns_rdatatype_ns,
+ 0, &ns_exists));
+ CHECK(rrset_exists(db, newver, name,
+ dns_rdatatype_dname, 0,
+ &dname_exists));
+
+ exists = ns_exists || dname_exists;
+ existed = ns_existed || dname_existed;
+ if (exists == existed)
+ goto nextname;
+ /*
+ * There was a delegation change. Mark all subdomains
+ * of t->name as potentially needing a NSEC3 update.
+ */
+ CHECK(namelist_append_subdomain(db, name,
+ &state->affected));
+
+ nextname:
+ while (t != NULL && dns_name_equal(&t->name, name))
+ t = ISC_LIST_NEXT(t, link);
+ }
+
+ /* FALLTHROUGH */
+ case process_nsec3:
+ state->state = process_nsec3;
+ while ((t = ISC_LIST_HEAD(state->affected.tuples)) != NULL) {
+ dns_name_t *name = &t->name;
+
+ unsecure = false; /* Silence compiler warning. */
+ CHECK(is_active(db, newver, name, &flag, &cut,
+ &unsecure));
+
+ if (!flag) {
+ CHECK(delete_if(rrsig_p, db, newver, name,
+ dns_rdatatype_any, 0, NULL,
+ diff));
+ CHECK(dns_nsec3_delnsec3sx(db, newver, name,
+ privatetype,
+ &state->nsec_diff));
+ } else {
+ CHECK(add_exposed_sigs(log, zone, db, newver,
+ name, cut,
+ &state->sig_diff,
+ state->zone_keys,
+ state->nkeys,
+ state->inception,
+ state->expire,
+ state->check_ksk,
+ state->keyset_kskonly,
+ &sigs));
+ CHECK(dns_nsec3_addnsec3sx(db, newver, name,
+ state->nsecttl,
+ unsecure,
+ privatetype,
+ &state->nsec_diff));
+ }
+ ISC_LIST_UNLINK(state->affected.tuples, t, link);
+ ISC_LIST_APPEND(state->work.tuples, t, link);
+ if (state != &mystate && sigs > maxsigs)
+ return (DNS_R_CONTINUE);
+ }
+ ISC_LIST_APPENDLIST(state->affected.tuples,
+ state->work.tuples, link);
+
+ /*
+ * Minimize the set of NSEC3 updates so that we don't
+ * have to regenerate the RRSIG NSEC3s for NSEC3s that were
+ * replaced with identical ones.
+ */
+ while ((t = ISC_LIST_HEAD(state->nsec_diff.tuples)) != NULL) {
+ ISC_LIST_UNLINK(state->nsec_diff.tuples, t, link);
+ dns_diff_appendminimal(&state->nsec_mindiff, &t);
+ }
+
+ update_log(log, zone, ISC_LOG_DEBUG(3),
+ "signing rebuilt NSEC3 chain");
+
+ /* FALLTHROUGH */
+ case sign_nsec3:
+ state->state = sign_nsec3;
+ /* Update RRSIG NSEC3s. */
+ while ((t = ISC_LIST_HEAD(state->nsec_mindiff.tuples)) != NULL)
+ {
+ if (t->op == DNS_DIFFOP_DEL) {
+ CHECK(delete_if(true_p, db, newver, &t->name,
+ dns_rdatatype_rrsig,
+ dns_rdatatype_nsec3,
+ NULL, &state->sig_diff));
+ } else if (t->op == DNS_DIFFOP_ADD) {
+ CHECK(add_sigs(log, zone, db, newver, &t->name,
+ dns_rdatatype_nsec3,
+ &state->sig_diff,
+ state->zone_keys,
+ state->nkeys, state->inception,
+ state->expire, state->check_ksk,
+ state->keyset_kskonly));
+ sigs++;
+ } else {
+ INSIST(0);
+ }
+ ISC_LIST_UNLINK(state->nsec_mindiff.tuples, t, link);
+ ISC_LIST_APPEND(state->work.tuples, t, link);
+ if (state != &mystate && sigs > maxsigs)
+ return (DNS_R_CONTINUE);
+ }
+ ISC_LIST_APPENDLIST(state->nsec_mindiff.tuples,
+ state->work.tuples, link);
+
+ /* Record our changes for the journal. */
+ while ((t = ISC_LIST_HEAD(state->sig_diff.tuples)) != NULL) {
+ ISC_LIST_UNLINK(state->sig_diff.tuples, t, link);
+ dns_diff_appendminimal(diff, &t);
+ }
+ while ((t = ISC_LIST_HEAD(state->nsec_mindiff.tuples)) != NULL)
+ {
+ ISC_LIST_UNLINK(state->nsec_mindiff.tuples, t, link);
+ dns_diff_appendminimal(diff, &t);
+ }
+
+ INSIST(ISC_LIST_EMPTY(state->sig_diff.tuples));
+ INSIST(ISC_LIST_EMPTY(state->nsec_diff.tuples));
+ INSIST(ISC_LIST_EMPTY(state->nsec_mindiff.tuples));
+ break;
+ default:
+ INSIST(0);
+ }
+
+ failure:
+ if (node != NULL) {
+ dns_db_detachnode(db, &node);
+ }
+
+ dns_diff_clear(&state->sig_diff);
+ dns_diff_clear(&state->nsec_diff);
+ dns_diff_clear(&state->nsec_mindiff);
+
+ dns_diff_clear(&state->affected);
+ dns_diff_clear(&state->diffnames);
+ dns_diff_clear(&state->work);
+
+ for (i = 0; i < state->nkeys; i++)
+ dst_key_free(&state->zone_keys[i]);
+
+ if (state != &mystate && state != NULL) {
+ *statep = NULL;
+ state->magic = 0;
+ isc_mem_put(diff->mctx, state, sizeof(*state));
+ }
+
+ return (result);
+}
+
+static isc_stdtime_t
+epoch_to_yyyymmdd(time_t when) {
+ struct tm *tm;
+
+#if defined(ISC_PLATFORM_USETHREADS) && !defined(WIN32)
+ struct tm tm0;
+ tm = localtime_r(&when, &tm0);
+#else
+ tm = localtime(&when);
+#endif
+ return (((tm->tm_year + 1900) * 10000) +
+ ((tm->tm_mon + 1) * 100) + tm->tm_mday);
+}
+
+uint32_t
+dns_update_soaserial(uint32_t serial, dns_updatemethod_t method) {
+ isc_stdtime_t now;
+ uint32_t new_serial;
+
+ switch (method) {
+ case dns_updatemethod_none:
+ return (serial);
+ case dns_updatemethod_unixtime:
+ isc_stdtime_get(&now);
+ if (now != 0 && isc_serial_gt(now, serial))
+ return (now);
+ break;
+ case dns_updatemethod_date:
+ isc_stdtime_get(&now);
+ new_serial = epoch_to_yyyymmdd((time_t) now) * 100;
+ if (new_serial != 0 && isc_serial_gt(new_serial, serial))
+ return (new_serial);
+ case dns_updatemethod_increment:
+ break;
+ }
+
+ /* RFC1982 */
+ serial = (serial + 1) & 0xFFFFFFFF;
+ if (serial == 0)
+ serial = 1;
+
+ return (serial);
+}
diff --git a/lib/dns/validator.c b/lib/dns/validator.c
new file mode 100644
index 0000000..9ae11a2
--- /dev/null
+++ b/lib/dns/validator.c
@@ -0,0 +1,3985 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.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/dnssec.h>
+#include <dns/ds.h>
+#include <dns/events.h>
+#include <dns/keytable.h>
+#include <dns/keyvalues.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/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 /*%< Canceled. */
+#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 FOUNDNODATA(val) ((val->attributes & VALATTR_FOUNDNODATA) != 0)
+#define FOUNDNOQNAME(val) ((val->attributes & VALATTR_FOUNDNOQNAME) != 0)
+#define FOUNDNOWILDCARD(val) ((val->attributes & VALATTR_FOUNDNOWILDCARD) != 0)
+#define FOUNDCLOSEST(val) ((val->attributes & VALATTR_FOUNDCLOSEST) != 0)
+#define FOUNDOPTOUT(val) ((val->attributes & VALATTR_FOUNDOPTOUT) != 0)
+
+#define SHUTDOWN(v) (((v)->attributes & VALATTR_SHUTDOWN) != 0)
+#define CANCELED(v) (((v)->attributes & VALATTR_CANCELED) != 0)
+
+#define NEGATIVE(r) (((r)->attributes & DNS_RDATASETATTR_NEGATIVE) != 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, bool resume);
+
+static isc_result_t
+validatezonekey(dns_validator_t *val);
+
+static isc_result_t
+nsecvalidate(dns_validator_t *val, bool resume);
+
+static isc_result_t
+proveunsecure(dns_validator_t *val, bool have_ds,
+ bool 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(void *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, bool 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, const char *where) {
+ validator_log(val, ISC_LOG_DEBUG(3), "marking as answer (%s)", where);
+ if (val->event->rdataset != NULL)
+ dns_rdataset_settrust(val->event->rdataset, dns_trust_answer);
+ if (val->event->sigrdataset != NULL)
+ dns_rdataset_settrust(val->event->sigrdataset,
+ dns_trust_answer);
+}
+
+static inline void
+marksecure(dns_validatorevent_t *event) {
+ dns_rdataset_settrust(event->rdataset, dns_trust_secure);
+ if (event->sigrdataset != NULL)
+ dns_rdataset_settrust(event->sigrdataset, dns_trust_secure);
+ event->secure = true;
+}
+
+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 bool
+exit_check(dns_validator_t *val) {
+ /*
+ * Caller must be holding the lock.
+ */
+ if (!SHUTDOWN(val))
+ return (false);
+
+ INSIST(val->event == NULL);
+
+ if (val->fetch != NULL || val->subvalidator != NULL)
+ return (false);
+
+ return (true);
+}
+
+/*
+ * Check that we have atleast one supported algorithm in the DLV RRset.
+ */
+static inline bool
+dlv_algorithm_supported(dns_validator_t *val) {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_dlv_t dlv;
+ isc_result_t result;
+
+ for (result = dns_rdataset_first(&val->dlv);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&val->dlv)) {
+ dns_rdata_reset(&rdata);
+ dns_rdataset_current(&val->dlv, &rdata);
+ result = dns_rdata_tostruct(&rdata, &dlv, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ if (!dns_resolver_algorithm_supported(val->view->resolver,
+ val->event->name,
+ dlv.algorithm))
+ continue;
+
+ if (!dns_resolver_ds_digest_supported(val->view->resolver,
+ val->event->name,
+ dlv.digest_type))
+ continue;
+
+ return (true);
+ }
+ return (false);
+}
+
+/*%
+ * 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 bool
+isdelegation(dns_name_t *name, dns_rdataset_t *rdataset,
+ isc_result_t dbresult)
+{
+ dns_fixedname_t fixed;
+ 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;
+ bool 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 (false);
+ }
+
+ INSIST(set.type == dns_rdatatype_nsec);
+
+ found = 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 = false;
+ dns_name_init(&nsec3name, NULL);
+ dns_fixedname_init(&fixed);
+ dns_name_downcase(name, dns_fixedname_name(&fixed), NULL);
+ name = dns_fixedname_name(&fixed);
+ 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_base32hexnp_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 (true);
+ }
+ }
+ dns_rdataset_disassociate(&set);
+ }
+ return (found);
+}
+
+/*%
+ * We have been asked 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;
+ bool want_destroy;
+ isc_result_t result;
+ isc_result_t eresult;
+ isc_result_t saved_result;
+ dns_fetch_t *fetch;
+
+ 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);
+
+ INSIST(val->event != NULL);
+
+ validator_log(val, ISC_LOG_DEBUG(3), "in fetch_callback_validator");
+ LOCK(&val->lock);
+ fetch = val->fetch;
+ val->fetch = NULL;
+ 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 %s",
+ dns_trust_totext(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, true);
+ 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, false, false);
+ if (result == DNS_R_NOTINSECURE)
+ result = saved_result;
+ }
+ 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_BROKENCHAIN);
+ }
+ want_destroy = exit_check(val);
+ UNLOCK(&val->lock);
+ if (fetch != NULL)
+ dns_resolver_destroyfetch(&fetch);
+ 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;
+ bool want_destroy;
+ isc_result_t result;
+ isc_result_t eresult;
+ dns_fetch_t *fetch;
+
+ 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);
+
+ INSIST(val->event != NULL);
+
+ validator_log(val, ISC_LOG_DEBUG(3), "in dsfetched");
+ LOCK(&val->lock);
+ fetch = val->fetch;
+ val->fetch = NULL;
+ 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 %s",
+ dns_trust_totext(rdataset->trust));
+ val->dsset = &val->frdataset;
+ result = validatezonekey(val);
+ if (result != DNS_R_WAIT)
+ validator_done(val, result);
+ } else if (eresult == DNS_R_CNAME ||
+ 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, false, 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_BROKENCHAIN);
+ }
+ want_destroy = exit_check(val);
+ UNLOCK(&val->lock);
+ if (fetch != NULL)
+ dns_resolver_destroyfetch(&fetch);
+ 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;
+ bool want_destroy;
+ isc_result_t result;
+ isc_result_t eresult;
+ dns_fetch_t *fetch;
+
+ 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);
+
+ INSIST(val->event != NULL);
+
+ validator_log(val, ISC_LOG_DEBUG(3), "in dsfetched2: %s",
+ dns_result_totext(eresult));
+ LOCK(&val->lock);
+ fetch = val->fetch;
+ val->fetch = NULL;
+ if (CANCELED(val)) {
+ validator_done(val, ISC_R_CANCELED);
+ } else if (eresult == DNS_R_CNAME ||
+ 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 (eresult != DNS_R_CNAME &&
+ isdelegation(tname, &val->frdataset, eresult)) {
+ if (val->mustbesecure) {
+ validator_log(val, ISC_LOG_WARNING,
+ "must be secure failure, no DS"
+ " and this is a delegation");
+ validator_done(val, DNS_R_MUSTBESECURE);
+ } else if (val->view->dlv == NULL || DLVTRIED(val)) {
+ markanswer(val, "dsfetched2");
+ validator_done(val, ISC_R_SUCCESS);
+ } else {
+ result = startfinddlvsep(val, tname);
+ if (result != DNS_R_WAIT)
+ validator_done(val, result);
+ }
+ } else {
+ result = proveunsecure(val, false, 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, (eresult == ISC_R_SUCCESS),
+ 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 (fetch != NULL)
+ dns_resolver_destroyfetch(&fetch);
+ 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;
+ bool want_destroy;
+ isc_result_t result;
+ isc_result_t eresult;
+ isc_result_t saved_result;
+
+ 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 %s",
+ dns_trust_totext(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, true);
+ 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, false, false);
+ if (result == DNS_R_NOTINSECURE)
+ result = saved_result;
+ }
+ if (result != DNS_R_WAIT)
+ validator_done(val, result);
+ } else {
+ if (eresult != DNS_R_BROKENCHAIN) {
+ if (dns_rdataset_isassociated(&val->frdataset))
+ dns_rdataset_expire(&val->frdataset);
+ if (dns_rdataset_isassociated(&val->fsigrdataset))
+ dns_rdataset_expire(&val->fsigrdataset);
+ }
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "keyvalidated: got %s",
+ isc_result_totext(eresult));
+ validator_done(val, DNS_R_BROKENCHAIN);
+ }
+ 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;
+ bool 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) {
+ bool have_dsset;
+ dns_name_t *name;
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "%s with trust %s",
+ val->frdataset.type == dns_rdatatype_ds ?
+ "dsset" : "ds non-existance",
+ dns_trust_totext(val->frdataset.trust));
+ have_dsset = (val->frdataset.type == dns_rdatatype_ds);
+ name = dns_fixedname_name(&val->fname);
+ if ((val->attributes & VALATTR_INSECURITY) != 0 &&
+ val->frdataset.covers == dns_rdatatype_ds &&
+ NEGATIVE(&val->frdataset) &&
+ isdelegation(name, &val->frdataset, DNS_R_NCACHENXRRSET)) {
+ if (val->mustbesecure) {
+ validator_log(val, ISC_LOG_WARNING,
+ "must be secure failure, no DS "
+ "and this is a delegation");
+ result = DNS_R_MUSTBESECURE;
+ } else if (val->view->dlv == NULL || DLVTRIED(val)) {
+ markanswer(val, "dsvalidated");
+ result = ISC_R_SUCCESS;;
+ } else
+ result = startfinddlvsep(val, name);
+ } else if ((val->attributes & VALATTR_INSECURITY) != 0) {
+ result = proveunsecure(val, have_dsset, true);
+ } else
+ result = validatezonekey(val);
+ if (result != DNS_R_WAIT)
+ validator_done(val, result);
+ } else {
+ if (eresult != DNS_R_BROKENCHAIN) {
+ if (dns_rdataset_isassociated(&val->frdataset))
+ dns_rdataset_expire(&val->frdataset);
+ if (dns_rdataset_isassociated(&val->fsigrdataset))
+ dns_rdataset_expire(&val->fsigrdataset);
+ }
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "dsvalidated: got %s",
+ isc_result_totext(eresult));
+ validator_done(val, DNS_R_BROKENCHAIN);
+ }
+ want_destroy = exit_check(val);
+ UNLOCK(&val->lock);
+ if (want_destroy)
+ destroy(val);
+}
+
+/*%
+ * Callback when the CNAME record has been validated.
+ *
+ * Resumes validation of the unsecure zone proof.
+ */
+static void
+cnamevalidated(isc_task_t *task, isc_event_t *event) {
+ dns_validatorevent_t *devent;
+ dns_validator_t *val;
+ bool 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);
+ INSIST((val->attributes & VALATTR_INSECURITY) != 0);
+
+ validator_log(val, ISC_LOG_DEBUG(3), "in cnamevalidated");
+ 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), "cname with trust %s",
+ dns_trust_totext(val->frdataset.trust));
+ result = proveunsecure(val, false, true);
+ if (result != DNS_R_WAIT)
+ validator_done(val, result);
+ } else {
+ if (eresult != DNS_R_BROKENCHAIN) {
+ if (dns_rdataset_isassociated(&val->frdataset))
+ dns_rdataset_expire(&val->frdataset);
+ if (dns_rdataset_isassociated(&val->fsigrdataset))
+ dns_rdataset_expire(&val->fsigrdataset);
+ }
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "cnamevalidated: got %s",
+ isc_result_totext(eresult));
+ validator_done(val, DNS_R_BROKENCHAIN);
+ }
+ want_destroy = exit_check(val);
+ UNLOCK(&val->lock);
+ if (want_destroy)
+ destroy(val);
+}
+
+/*%
+ * 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;
+ bool want_destroy;
+ isc_result_t result;
+ bool exists, data;
+
+ UNUSED(task);
+ INSIST(event->ev_type == DNS_EVENT_VALIDATORDONE);
+
+ devent = (dns_validatorevent_t *)event;
+ rdataset = devent->rdataset;
+ 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 == DNS_R_BROKENCHAIN)
+ val->authfail++;
+ if (result == ISC_R_CANCELED)
+ validator_done(val, result);
+ else {
+ result = nsecvalidate(val, 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 = true;
+
+ if (rdataset->type == dns_rdatatype_nsec &&
+ rdataset->trust == dns_trust_secure &&
+ (NEEDNODATA(val) || NEEDNOQNAME(val)) &&
+ !FOUNDNODATA(val) && !FOUNDNOQNAME(val) &&
+ dns_nsec_noexistnodata(val->event->type, val->event->name,
+ devent->name, rdataset, &exists,
+ &data, wild, validator_log, val)
+ == ISC_R_SUCCESS)
+ {
+ if (exists && !data) {
+ val->attributes |= VALATTR_FOUNDNODATA;
+ if (NEEDNODATA(val))
+ proofs[DNS_VALIDATOR_NODATAPROOF] =
+ devent->name;
+ }
+ if (!exists) {
+ dns_name_t *closest;
+ unsigned int clabels;
+
+ val->attributes |= VALATTR_FOUNDNOQNAME;
+
+ closest = dns_fixedname_name(&val->closest);
+ clabels = dns_name_countlabels(closest);
+ /*
+ * If we are validating a wildcard response
+ * clabels will not be zero. We then need
+ * to check if the generated wilcard from
+ * dns_nsec_noexistnodata is consistent with
+ * the wildcard used to generate the response.
+ */
+ if (clabels == 0 ||
+ dns_name_countlabels(wild) == clabels + 1)
+ 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, 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
+ * \li DNS_R_BROKENCHAIN
+ */
+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;
+ isc_time_t now;
+ char buf1[DNS_NAME_FORMATSIZE];
+ char buf2[DNS_NAME_FORMATSIZE];
+ char buf3[DNS_NAME_FORMATSIZE];
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char typebuf[DNS_RDATATYPE_FORMATSIZE];
+
+ if (dns_rdataset_isassociated(&val->frdataset))
+ dns_rdataset_disassociate(&val->frdataset);
+ if (dns_rdataset_isassociated(&val->fsigrdataset))
+ dns_rdataset_disassociate(&val->fsigrdataset);
+
+ if (isc_time_now(&now) == ISC_R_SUCCESS &&
+ dns_resolver_getbadcache(val->view->resolver, name, type, &now)) {
+
+ dns_name_format(name, namebuf, sizeof(namebuf));
+ dns_rdatatype_format(type, typebuf, sizeof(typebuf));
+ validator_log(val, ISC_LOG_INFO, "bad cache hit (%s/%s)",
+ namebuf, typebuf);
+ return (DNS_R_BROKENCHAIN);
+ }
+
+ options = DNS_DBFIND_PENDINGOK;
+ if (type == dns_rdatatype_dlv)
+ options |= DNS_DBFIND_COVERINGNSEC;
+ foundname = dns_fixedname_initname(&fixedname);
+ result = dns_view_find(val->view, name, type, 0, options,
+ 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 %s",
+ dns_trust_totext(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 bool
+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 (true);
+ }
+ }
+ return (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)
+{
+ unsigned int fopts = 0;
+
+ 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)) {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "deadlock found (create_fetch)");
+ return (DNS_R_NOVALIDSIG);
+ }
+
+ if ((val->options & DNS_VALIDATOR_NOCDFLAG) != 0)
+ fopts |= DNS_FETCHOPT_NOCDFLAG;
+
+ if ((val->options & DNS_VALIDATOR_NONTA) != 0)
+ fopts |= DNS_FETCHOPT_NONTA;
+
+ validator_logcreate(val, name, type, caller, "fetch");
+ return (dns_resolver_createfetch(val->view->resolver, name, type,
+ NULL, NULL, NULL, fopts,
+ 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;
+ unsigned int vopts = 0;
+
+ if (check_deadlock(val, name, type, rdataset, sigrdataset)) {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "deadlock found (create_validator)");
+ return (DNS_R_NOVALIDSIG);
+ }
+
+ /* OK to clear other options, but preserve NOCDFLAG and NONTA. */
+ vopts |= (val->options & (DNS_VALIDATOR_NOCDFLAG|DNS_VALIDATOR_NONTA));
+
+ validator_logcreate(val, name, type, caller, "validator");
+ result = dns_validator_create(val->view, name, type,
+ rdataset, sigrdataset, NULL, vopts,
+ 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;
+ bool foundold;
+
+ if (oldkey == NULL)
+ foundold = true;
+ else {
+ foundold = 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) == true)
+ {
+ foundold = 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 generated 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 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 ((DNS_TRUST_PENDING(val->frdataset.trust) ||
+ DNS_TRUST_ANSWER(val->frdataset.trust)) &&
+ dns_rdataset_isassociated(&val->fsigrdataset))
+ {
+ /*
+ * We know the key but haven't validated it yet or
+ * we have a key of trust answer but a DS/DLV
+ * record for the zone may have been added.
+ */
+ 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 (DNS_TRUST_PENDING(val->frdataset.trust)) {
+ /*
+ * 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 %s",
+ dns_trust_totext(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;
+ } else if (result == DNS_R_BROKENCHAIN)
+ return (result);
+
+ 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 bool
+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;
+ dns_name_t *name;
+ isc_result_t result;
+ dst_key_t *dstkey;
+ isc_mem_t *mctx;
+ bool answer = false;
+
+ rdataset = val->event->rdataset;
+ sigrdataset = val->event->sigrdataset;
+ name = val->event->name;
+ mctx = val->view->mctx;
+
+ if (rdataset->type == dns_rdatatype_cname ||
+ rdataset->type == dns_rdatatype_dname)
+ return (answer);
+
+ 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 ||
+ !dns_name_equal(name, &sig.signer))
+ continue;
+
+ dstkey = NULL;
+ result = dns_dnssec_keyfromrdata(name, &rdata, mctx,
+ &dstkey);
+ if (result != ISC_R_SUCCESS)
+ continue;
+
+ result = dns_dnssec_verify3(name, rdataset, dstkey,
+ true,
+ val->view->maxbits,
+ mctx, &sigrdata, NULL);
+ dst_key_free(&dstkey);
+ if (result != ISC_R_SUCCESS)
+ continue;
+ if ((key.flags & DNS_KEYFLAG_REVOKE) == 0) {
+ answer = true;
+ continue;
+ }
+ dns_view_untrust(val->view, name, &key, mctx);
+ }
+ }
+ return (answer);
+}
+
+/*%
+ * 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,
+ uint16_t keyid)
+{
+ isc_result_t result;
+ dns_fixedname_t fixed;
+ bool ignore = false;
+ dns_name_t *wild;
+
+ val->attributes |= VALATTR_TRIEDVERIFY;
+ wild = dns_fixedname_initname(&fixed);
+ again:
+ result = dns_dnssec_verify3(val->event->name, val->event->rdataset,
+ key, ignore, val->view->maxbits,
+ val->view->mctx, rdata, wild);
+ if ((result == DNS_R_SIGEXPIRED || result == DNS_R_SIGFUTURE) &&
+ val->view->acceptexpired)
+ {
+ ignore = 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 if (result == DNS_R_SIGEXPIRED || result == DNS_R_SIGFUTURE)
+ validator_log(val, ISC_LOG_INFO,
+ "verify failed due to bad signature (keyid=%u): "
+ "%s", keyid, isc_result_totext(result));
+ 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, wild)) {
+ dns_name_t *closest;
+ unsigned int labels;
+
+ /*
+ * Compute the closest encloser in case we need it
+ * for the NSEC3 NOQNAME proof.
+ */
+ closest = dns_fixedname_name(&val->closest);
+ dns_name_copy(wild, closest, NULL);
+ labels = dns_name_countlabels(closest) - 1;
+ dns_name_getlabelsequence(closest, 1, labels, closest);
+ 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, bool resume) {
+ isc_result_t result, vresult = DNS_R_NOVALIDSIG;
+ 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)) {
+ resume = false;
+ 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);
+ }
+
+ /*
+ * There isn't a secure DNSKEY for this signature so move
+ * onto the next RRSIG.
+ */
+ if (val->key == NULL) {
+ resume = false;
+ continue;
+ }
+
+ do {
+ vresult = verify(val, val->key, &rdata,
+ val->siginfo->keyid);
+ if (vresult == 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);
+ if (val->key == NULL)
+ break;
+ } else {
+ if (get_dst_key(val, val->siginfo, val->keyset)
+ != ISC_R_SUCCESS)
+ break;
+ }
+ } while (1);
+ if (vresult != ISC_R_SUCCESS)
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "failed to verify rdataset");
+ else {
+ dns_rdataset_trimttl(event->rdataset,
+ event->sigrdataset,
+ val->siginfo, val->start,
+ val->view->acceptexpired);
+ }
+
+ 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 (NEEDNOQNAME(val)) {
+ 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, false));
+ } else if (vresult == ISC_R_SUCCESS) {
+ marksecure(event);
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "marking as secure, "
+ "noqname proof not needed");
+ return (ISC_R_SUCCESS);
+ } else {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "verify failure: %s",
+ isc_result_totext(result));
+ resume = 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 (vresult);
+}
+
+/*%
+ * Check whether this DNSKEY (keyrdata) signed the DNSKEY RRset
+ * (val->event->rdataset).
+ */
+static isc_result_t
+checkkey(dns_validator_t *val, dns_rdata_t *keyrdata, uint16_t keyid,
+ dns_secalg_t algorithm)
+{
+ dns_rdata_rrsig_t sig;
+ dst_key_t *dstkey = NULL;
+ isc_result_t result;
+
+ for (result = dns_rdataset_first(val->event->sigrdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(val->event->sigrdataset))
+ {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+
+ dns_rdataset_current(val->event->sigrdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &sig, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ if (keyid != sig.keyid || algorithm != sig.algorithm)
+ continue;
+ if (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, &rdata, sig.keyid);
+ if (result == ISC_R_SUCCESS)
+ break;
+ }
+ if (dstkey != NULL)
+ dst_key_free(&dstkey);
+ return (result);
+}
+
+/*%
+ * Find the DNSKEY that corresponds to the DS.
+ */
+static isc_result_t
+keyfromds(dns_validator_t *val, dns_rdataset_t *rdataset, dns_rdata_t *dsrdata,
+ uint8_t digest, uint16_t keyid, dns_secalg_t algorithm,
+ dns_rdata_t *keyrdata)
+{
+ dns_keytag_t keytag;
+ dns_rdata_dnskey_t key;
+ isc_result_t result;
+ unsigned char dsbuf[DNS_DS_BUFFERSIZE];
+
+ for (result = dns_rdataset_first(rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(rdataset))
+ {
+ dns_rdata_t newdsrdata = DNS_RDATA_INIT;
+
+ dns_rdata_reset(keyrdata);
+ dns_rdataset_current(rdataset, keyrdata);
+ result = dns_rdata_tostruct(keyrdata, &key, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ keytag = compute_keytag(keyrdata, &key);
+ if (keyid != keytag || algorithm != key.algorithm)
+ continue;
+ dns_rdata_reset(&newdsrdata);
+ result = dns_ds_buildrdata(val->event->name, keyrdata, digest,
+ dsbuf, &newdsrdata);
+ if (result != ISC_R_SUCCESS) {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "dns_ds_buildrdata() -> %s",
+ dns_result_totext(result));
+ continue;
+ }
+ if (dns_rdata_compare(dsrdata, &newdsrdata) == 0)
+ break;
+ }
+ return (result);
+}
+
+/*%
+ * 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_rdata_dlv_t dlv;
+ dns_rdata_t dlvrdata = DNS_RDATA_INIT;
+ dns_rdata_t keyrdata = DNS_RDATA_INIT;
+ dns_rdataset_t trdataset;
+ bool supported_algorithm;
+ isc_result_t result;
+ char digest_types[256];
+
+ 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 = false;
+
+ /*
+ * If DNS_DSDIGEST_SHA256 or DNS_DSDIGEST_SHA384 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 or DNS_DSDIGEST_SHA384 is present.
+ */
+ memset(digest_types, 1, sizeof(digest_types));
+ 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_ds_digest_supported(val->view->resolver,
+ val->event->name,
+ dlv.digest_type))
+ continue;
+
+ 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) ||
+ (dlv.digest_type == DNS_DSDIGEST_SHA384 &&
+ dlv.length == ISC_SHA384_DIGESTLENGTH))
+ {
+ digest_types[DNS_DSDIGEST_SHA1] = 0;
+ 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 (digest_types[dlv.digest_type] == 0)
+ continue;
+
+ if (!dns_resolver_ds_digest_supported(val->view->resolver,
+ val->event->name,
+ dlv.digest_type))
+ continue;
+
+ if (!dns_resolver_algorithm_supported(val->view->resolver,
+ val->event->name,
+ dlv.algorithm))
+ continue;
+
+ supported_algorithm = true;
+
+ dns_rdataset_init(&trdataset);
+ dns_rdataset_clone(val->event->rdataset, &trdataset);
+
+ /*
+ * Convert to DLV to DS and find matching DNSKEY.
+ */
+ dlvrdata.type = dns_rdatatype_ds;
+ result = keyfromds(val, &trdataset, &dlvrdata,
+ dlv.digest_type, dlv.key_tag,
+ dlv.algorithm, &keyrdata);
+ if (result != ISC_R_SUCCESS) {
+ dns_rdataset_disassociate(&trdataset);
+ 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");
+ /*
+ * Check that this DNSKEY signed the DNSKEY rrset.
+ */
+ result = checkkey(val, &keyrdata, dlv.key_tag, dlv.algorithm);
+
+ 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) {
+ marksecure(val->event);
+ validator_log(val, ISC_LOG_DEBUG(3), "marking as secure (dlv)");
+ return (result);
+ } else if (result == ISC_R_NOMORE && !supported_algorithm) {
+ if (val->mustbesecure) {
+ validator_log(val, ISC_LOG_WARNING,
+ "must be secure failure,"
+ "no supported algorithm/digest (dlv)");
+ return (DNS_R_MUSTBESECURE);
+ }
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "no supported algorithm/digest (dlv)");
+ markanswer(val, "dlv_validatezonekey (2)");
+ return (ISC_R_SUCCESS);
+ } else
+ return (DNS_R_NOVALIDSIG);
+}
+
+/*%
+ * Attempts positive response validation of an RRset containing zone keys
+ * (i.e. a DNSKEY 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
+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 keyrdata = DNS_RDATA_INIT;
+ dns_rdata_t sigrdata = DNS_RDATA_INIT;
+ char namebuf[DNS_NAME_FORMATSIZE];
+ dns_rdata_ds_t ds;
+ dns_rdata_rrsig_t sig;
+ dst_key_t *dstkey;
+ bool supported_algorithm;
+ bool atsep = false;
+ char digest_types[256];
+
+ /*
+ * 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) {
+
+ /*
+ * We have a dlv sep. Skip looking up the SEP from
+ * {trusted,managed}-keys. If the dlv sep is for the
+ * root then it will have been handled above so we don't
+ * need to check whether val->event->name is "." prior to
+ * looking up the DS.
+ */
+ if (val->havedlvsep)
+ goto find_ds;
+
+ /*
+ * 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;
+ dns_fixedname_t fixed;
+ dns_name_t *found;
+
+ found = dns_fixedname_initname(&fixed);
+ 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 == ISC_R_NOTFOUND &&
+ dns_keytable_finddeepestmatch(val->keytable,
+ val->event->name, found) != ISC_R_SUCCESS) {
+ if (val->mustbesecure) {
+ validator_log(val, ISC_LOG_WARNING,
+ "must be secure failure, "
+ "not beneath secure root");
+ return (DNS_R_MUSTBESECURE);
+ } else
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "not beneath secure root");
+ if (val->view->dlv == NULL) {
+ markanswer(val, "validatezonekey (1)");
+ return (ISC_R_SUCCESS);
+ }
+ return (startfinddlvsep(val, dns_rootname));
+ }
+ if (result == DNS_R_PARTIALMATCH ||
+ result == ISC_R_SUCCESS)
+ atsep = true;
+ while (result == ISC_R_SUCCESS) {
+ dns_keynode_t *nextnode = NULL;
+ dstkey = dns_keynode_key(keynode);
+ if (dstkey == NULL) {
+ dns_keytable_detachkeynode(
+ val->keytable,
+ &keynode);
+ break;
+ }
+ 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) {
+ marksecure(event);
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "signed by trusted key; "
+ "marking as secure");
+ return (result);
+ }
+ }
+
+ 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_NOTICE,
+ "unable to find a DNSKEY which verifies "
+ "the DNSKEY RRset and also matches a "
+ "trusted key for '%s'",
+ namebuf);
+ return (DNS_R_NOVALIDKEY);
+ }
+
+ /*
+ * 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) {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "root key failed to validate");
+ return (DNS_R_NOVALIDSIG);
+ } else {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "no trusted root key");
+ return (DNS_R_NOVALIDDS);
+ }
+ }
+ find_ds:
+ /*
+ * 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 ((DNS_TRUST_PENDING(val->frdataset.trust) ||
+ DNS_TRUST_ANSWER(val->frdataset.trust)) &&
+ 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 (DNS_TRUST_PENDING(val->frdataset.trust)) {
+ /*
+ * 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;
+ POST(result);
+ }
+ } 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 ||
+ result == DNS_R_CNAME)
+ {
+ /*
+ * 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);
+ } else if (result == DNS_R_BROKENCHAIN)
+ return (result);
+ }
+
+ /*
+ * 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,"
+ " insecure DS");
+ return (DNS_R_MUSTBESECURE);
+ }
+ if (val->view->dlv == NULL || DLVTRIED(val)) {
+ markanswer(val, "validatezonekey (2)");
+ return (ISC_R_SUCCESS);
+ }
+ return (startfinddlvsep(val, val->event->name));
+ }
+
+ /*
+ * 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 = false;
+
+ /*
+ * If DNS_DSDIGEST_SHA256 or DNS_DSDIGEST_SHA384 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 or DNS_DSDIGEST_SHA384 is present.
+ */
+ memset(digest_types, 1, sizeof(digest_types));
+ 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_ds_digest_supported(val->view->resolver,
+ val->event->name,
+ ds.digest_type))
+ continue;
+
+ 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) ||
+ (ds.digest_type == DNS_DSDIGEST_SHA384 &&
+ ds.length == ISC_SHA384_DIGESTLENGTH))
+ {
+ digest_types[DNS_DSDIGEST_SHA1] = 0;
+ 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 (digest_types[ds.digest_type] == 0)
+ continue;
+
+ if (!dns_resolver_ds_digest_supported(val->view->resolver,
+ val->event->name,
+ ds.digest_type))
+ continue;
+
+ if (!dns_resolver_algorithm_supported(val->view->resolver,
+ val->event->name,
+ ds.algorithm))
+ continue;
+
+ supported_algorithm = true;
+
+ dns_rdataset_init(&trdataset);
+ dns_rdataset_clone(val->event->rdataset, &trdataset);
+
+ /*
+ * Find matching DNSKEY from DS.
+ */
+ result = keyfromds(val, &trdataset, &dsrdata, ds.digest_type,
+ ds.key_tag, ds.algorithm, &keyrdata);
+ if (result != ISC_R_SUCCESS) {
+ dns_rdataset_disassociate(&trdataset);
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "no DNSKEY matching DS");
+ continue;
+ }
+
+ /*
+ * Check that this DNSKEY signed the DNSKEY rrset.
+ */
+ result = checkkey(val, &keyrdata, ds.key_tag, ds.algorithm);
+
+ 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) {
+ marksecure(event);
+ validator_log(val, ISC_LOG_DEBUG(3), "marking as secure (DS)");
+ return (result);
+ } else if (result == ISC_R_NOMORE && !supported_algorithm) {
+ if (val->mustbesecure) {
+ validator_log(val, ISC_LOG_WARNING,
+ "must be secure failure, "
+ "no supported algorithm/digest (DS)");
+ return (DNS_R_MUSTBESECURE);
+ }
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "no supported algorithm/digest (DS)");
+ markanswer(val, "validatezonekey (3)");
+ return (ISC_R_SUCCESS);
+ } else {
+ validator_log(val, ISC_LOG_INFO,
+ "no valid signature found (DS)");
+ 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, false));
+
+ return (validatezonekey(val));
+}
+
+/*%
+ * val_rdataset_first and val_rdataset_next provide iteration methods
+ * that hide whether we are iterating across a message or a negative
+ * cache rdataset.
+ */
+static isc_result_t
+val_rdataset_first(dns_validator_t *val, dns_name_t **namep,
+ dns_rdataset_t **rdatasetp)
+{
+ dns_message_t *message = val->event->message;
+ isc_result_t result;
+
+ REQUIRE(rdatasetp != NULL);
+ REQUIRE(namep != NULL);
+ if (message == NULL) {
+ REQUIRE(*rdatasetp != NULL);
+ REQUIRE(*namep != NULL);
+ } else {
+ REQUIRE(*rdatasetp == NULL);
+ REQUIRE(*namep == NULL);
+ }
+
+ if (message != NULL) {
+ result = dns_message_firstname(message, DNS_SECTION_AUTHORITY);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ dns_message_currentname(message, DNS_SECTION_AUTHORITY, namep);
+ *rdatasetp = ISC_LIST_HEAD((*namep)->list);
+ INSIST(*rdatasetp != NULL);
+ } else {
+ result = dns_rdataset_first(val->event->rdataset);
+ if (result == ISC_R_SUCCESS)
+ dns_ncache_current(val->event->rdataset, *namep,
+ *rdatasetp);
+ }
+ return (result);
+}
+
+static isc_result_t
+val_rdataset_next(dns_validator_t *val, dns_name_t **namep,
+ dns_rdataset_t **rdatasetp)
+{
+ dns_message_t *message = val->event->message;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ REQUIRE(rdatasetp != NULL && *rdatasetp != NULL);
+ REQUIRE(namep != NULL && *namep != NULL);
+
+ if (message != NULL) {
+ dns_rdataset_t *rdataset = *rdatasetp;
+ rdataset = ISC_LIST_NEXT(rdataset, link);
+ if (rdataset == NULL) {
+ *namep = NULL;
+ result = dns_message_nextname(message,
+ DNS_SECTION_AUTHORITY);
+ if (result == ISC_R_SUCCESS) {
+ dns_message_currentname(message,
+ DNS_SECTION_AUTHORITY,
+ namep);
+ rdataset = ISC_LIST_HEAD((*namep)->list);
+ INSIST(rdataset != NULL);
+ }
+ }
+ *rdatasetp = rdataset;
+ } else {
+ dns_rdataset_disassociate(*rdatasetp);
+ result = dns_rdataset_next(val->event->rdataset);
+ if (result == ISC_R_SUCCESS)
+ dns_ncache_current(val->event->rdataset, *namep,
+ *rdatasetp);
+ }
+ return (result);
+}
+
+/*%
+ * 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, tname;
+ isc_result_t result;
+ bool exists, data;
+ char namebuf[DNS_NAME_FORMATSIZE];
+ dns_rdataset_t *rdataset, trdataset;
+
+ dns_name_init(&tname, NULL);
+ dns_rdataset_init(&trdataset);
+ 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);
+
+ if (val->event->message == NULL) {
+ name = &tname;
+ rdataset = &trdataset;
+ } else {
+ name = NULL;
+ rdataset = NULL;
+ }
+
+ for (result = val_rdataset_first(val, &name, &rdataset);
+ result == ISC_R_SUCCESS;
+ result = val_rdataset_next(val, &name, &rdataset))
+ {
+ if (rdataset->type != type ||
+ rdataset->trust != dns_trust_secure)
+ continue;
+
+ if (rdataset->type == dns_rdatatype_nsec &&
+ (NEEDNODATA(val) || NEEDNOWILDCARD(val)) &&
+ !FOUNDNODATA(val) && !FOUNDNOWILDCARD(val) &&
+ dns_nsec_noexistnodata(val->event->type, wild, name,
+ rdataset, &exists, &data, NULL,
+ validator_log, val)
+ == 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;
+ if (dns_rdataset_isassociated(&trdataset))
+ dns_rdataset_disassociate(&trdataset);
+ return (ISC_R_SUCCESS);
+ }
+
+ if (rdataset->type == dns_rdatatype_nsec3 &&
+ (NEEDNODATA(val) || NEEDNOWILDCARD(val)) &&
+ !FOUNDNODATA(val) && !FOUNDNOWILDCARD(val) &&
+ dns_nsec3_noexistnodata(val->event->type, wild, name,
+ rdataset, zonename, &exists, &data,
+ NULL, NULL, NULL, NULL, NULL, NULL,
+ validator_log, val)
+ == 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;
+ if (dns_rdataset_isassociated(&trdataset))
+ dns_rdataset_disassociate(&trdataset);
+ return (ISC_R_SUCCESS);
+ }
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ if (dns_rdataset_isassociated(&trdataset))
+ dns_rdataset_disassociate(&trdataset);
+ return (result);
+}
+
+static isc_result_t
+findnsec3proofs(dns_validator_t *val) {
+ dns_name_t *name, tname;
+ isc_result_t result;
+ bool exists, data, optout, unknown;
+ bool setclosest, setnearest, *setclosestp;
+ dns_fixedname_t fclosest, fnearest, fzonename;
+ dns_name_t *closest, *nearest, *zonename, *closestp;
+ dns_name_t **proofs = val->event->proofs;
+ dns_rdataset_t *rdataset, trdataset;
+
+ dns_name_init(&tname, NULL);
+ dns_rdataset_init(&trdataset);
+ closest = dns_fixedname_initname(&fclosest);
+ nearest = dns_fixedname_initname(&fnearest);
+ zonename = dns_fixedname_initname(&fzonename);
+
+ if (val->event->message == NULL) {
+ name = &tname;
+ rdataset = &trdataset;
+ } else {
+ name = NULL;
+ rdataset = NULL;
+ }
+
+ for (result = val_rdataset_first(val, &name, &rdataset);
+ result == ISC_R_SUCCESS;
+ result = val_rdataset_next(val, &name, &rdataset))
+ {
+ if (rdataset->type != dns_rdatatype_nsec3 ||
+ rdataset->trust != dns_trust_secure)
+ continue;
+
+ result = dns_nsec3_noexistnodata(val->event->type,
+ val->event->name, name,
+ rdataset, zonename, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, validator_log,
+ val);
+ if (result != ISC_R_IGNORE && result != ISC_R_SUCCESS) {
+ if (dns_rdataset_isassociated(&trdataset))
+ dns_rdataset_disassociate(&trdataset);
+ return (result);
+ }
+ }
+ if (result != ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ POST(result);
+
+ if (dns_name_countlabels(zonename) == 0)
+ return (ISC_R_SUCCESS);
+
+ /*
+ * If the val->closest is set then we want to use it otherwise
+ * we need to discover it.
+ */
+ if (dns_name_countlabels(dns_fixedname_name(&val->closest)) != 0) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+
+ dns_name_format(dns_fixedname_name(&val->closest),
+ namebuf, sizeof(namebuf));
+ validator_log(val, ISC_LOG_DEBUG(3), "closest encloser from "
+ "wildcard signature '%s'", namebuf);
+ dns_name_copy(dns_fixedname_name(&val->closest), closest, NULL);
+ closestp = NULL;
+ setclosestp = NULL;
+ } else {
+ closestp = closest;
+ setclosestp = &setclosest;
+ }
+
+ for (result = val_rdataset_first(val, &name, &rdataset);
+ result == ISC_R_SUCCESS;
+ result = val_rdataset_next(val, &name, &rdataset))
+ {
+ if (rdataset->type != dns_rdatatype_nsec3 ||
+ 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 = false;
+ optout = false;
+ unknown = false;
+ result = dns_nsec3_noexistnodata(val->event->type,
+ val->event->name,
+ name, rdataset, zonename,
+ &exists, &data, &optout,
+ &unknown, setclosestp,
+ &setnearest, closestp,
+ nearest, validator_log, val);
+ if (unknown)
+ val->attributes |= VALATTR_FOUNDUNKNOWN;
+ if (result != ISC_R_SUCCESS)
+ continue;
+ if (setclosest)
+ proofs[DNS_VALIDATOR_CLOSESTENCLOSER] = name;
+ 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 (FOUNDNOQNAME(val) && FOUNDCLOSEST(val) &&
+ ((NEEDNODATA(val) && !FOUNDNODATA(val)) || NEEDNOWILDCARD(val))) {
+ result = checkwildcard(val, dns_rdatatype_nsec3, zonename);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+ return (result);
+}
+
+/*%
+ * Validate the authority section records.
+ */
+static isc_result_t
+validate_authority(dns_validator_t *val, bool 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;
+
+ 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 = 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 &&
+ rdataset->type == dns_rdatatype_nsec &&
+ dns_name_equal(name, val->event->name))
+ {
+ dns_rdata_t nsec = DNS_RDATA_INIT;
+
+ 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,
+ "validate_authority");
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ val->authcount++;
+ return (DNS_R_WAIT);
+ }
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ return (result);
+}
+
+/*%
+ * Validate the ncache elements.
+ */
+static isc_result_t
+validate_ncache(dns_validator_t *val, bool resume) {
+ dns_name_t *name;
+ isc_result_t result;
+
+ if (!resume)
+ result = dns_rdataset_first(val->event->rdataset);
+ else
+ result = dns_rdataset_next(val->event->rdataset);
+
+ for (;
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(val->event->rdataset))
+ {
+ dns_rdataset_t *rdataset, *sigrdataset = NULL;
+
+ if (dns_rdataset_isassociated(&val->frdataset))
+ dns_rdataset_disassociate(&val->frdataset);
+ if (dns_rdataset_isassociated(&val->fsigrdataset))
+ dns_rdataset_disassociate(&val->fsigrdataset);
+
+ name = dns_fixedname_initname(&val->fname);
+ rdataset = &val->frdataset;
+ dns_ncache_current(val->event->rdataset, name, rdataset);
+
+ if (val->frdataset.type == dns_rdatatype_rrsig)
+ continue;
+
+ result = dns_ncache_getsigrdataset(val->event->rdataset, name,
+ rdataset->type,
+ &val->fsigrdataset);
+ if (result == ISC_R_SUCCESS)
+ sigrdataset = &val->fsigrdataset;
+
+ /*
+ * 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 &&
+ rdataset->type == dns_rdatatype_nsec &&
+ dns_name_equal(name, val->event->name))
+ {
+ dns_rdata_t nsec = DNS_RDATA_INIT;
+
+ 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,
+ "validate_ncache");
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ val->authcount++;
+ return (DNS_R_WAIT);
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ 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, bool resume) {
+ isc_result_t result;
+
+ if (resume)
+ validator_log(val, ISC_LOG_DEBUG(3), "resuming nsecvalidate");
+
+ if (val->event->message == NULL)
+ result = validate_ncache(val, resume);
+ else
+ result = validate_authority(val, resume);
+
+ 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 (!NEEDNODATA(val) && !NEEDNOWILDCARD(val) && NEEDNOQNAME(val)) {
+ if (!FOUNDNOQNAME(val))
+ findnsec3proofs(val);
+ if (FOUNDNOQNAME(val) && FOUNDCLOSEST(val) &&
+ !FOUNDOPTOUT(val)) {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "marking as secure, noqname proof found");
+ marksecure(val->event);
+ return (ISC_R_SUCCESS);
+ } else if (FOUNDOPTOUT(val) &&
+ dns_name_countlabels(dns_fixedname_name(&val->wild))
+ != 0) {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "optout proof found");
+ val->event->optout = true;
+ markanswer(val, "nsecvalidate (1)");
+ 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, "nsecvalidate (2)");
+ return (ISC_R_SUCCESS);
+ }
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "noqname proof not found");
+ return (DNS_R_NOVALIDNSEC);
+ }
+
+ if (!FOUNDNOQNAME(val) && !FOUNDNODATA(val))
+ findnsec3proofs(val);
+
+ /*
+ * Do we need to check for the wildcard?
+ */
+ if (FOUNDNOQNAME(val) && FOUNDCLOSEST(val) &&
+ ((NEEDNODATA(val) && !FOUNDNODATA(val)) || NEEDNOWILDCARD(val))) {
+ result = checkwildcard(val, dns_rdatatype_nsec, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+
+ if ((NEEDNODATA(val) && (FOUNDNODATA(val) || FOUNDOPTOUT(val))) ||
+ (NEEDNOQNAME(val) && FOUNDNOQNAME(val) &&
+ NEEDNOWILDCARD(val) && FOUNDNOWILDCARD(val) &&
+ FOUNDCLOSEST(val))) {
+ if ((val->attributes & VALATTR_FOUNDOPTOUT) != 0)
+ val->event->optout = true;
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "nonexistence proof(s) found");
+ if (val->event->message == NULL)
+ marksecure(val->event);
+ else
+ val->event->secure = true;
+ return (ISC_R_SUCCESS);
+ }
+
+ if (val->authfail != 0 && val->authcount == val->authfail)
+ return (DNS_R_BROKENCHAIN);
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "nonexistence proof(s) not found");
+ val->attributes |= VALATTR_INSECURITY;
+ return (proveunsecure(val, false, false));
+}
+
+static bool
+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_ds_digest_supported(val->view->resolver,
+ name, ds.digest_type) &&
+ dns_resolver_algorithm_supported(val->view->resolver,
+ name, ds.algorithm)) {
+ dns_rdata_reset(&dsrdata);
+ return (true);
+ }
+ dns_rdata_reset(&dsrdata);
+ }
+ return (false);
+}
+
+static void
+dlvvalidated(isc_task_t *task, isc_event_t *event) {
+ dns_validatorevent_t *devent;
+ dns_validator_t *val;
+ isc_result_t eresult;
+ bool want_destroy;
+
+ 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 dlvvalidated");
+ 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),
+ "dlvset with trust %s",
+ dns_trust_totext(val->frdataset.trust));
+ dns_rdataset_clone(&val->frdataset, &val->dlv);
+ val->havedlvsep = true;
+ if (dlv_algorithm_supported(val))
+ dlv_validator_start(val);
+ else {
+ markanswer(val, "dlvvalidated");
+ validator_done(val, ISC_R_SUCCESS);
+ }
+ } else {
+ if (eresult != DNS_R_BROKENCHAIN) {
+ if (dns_rdataset_isassociated(&val->frdataset))
+ dns_rdataset_expire(&val->frdataset);
+ if (dns_rdataset_isassociated(&val->fsigrdataset))
+ dns_rdataset_expire(&val->fsigrdataset);
+ }
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "dlvvalidated: got %s",
+ isc_result_totext(eresult));
+ validator_done(val, DNS_R_BROKENCHAIN);
+ }
+ want_destroy = exit_check(val);
+ UNLOCK(&val->lock);
+ if (want_destroy)
+ destroy(val);
+}
+
+/*%
+ * 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;
+ bool want_destroy;
+ isc_result_t eresult;
+ isc_result_t result;
+ dns_fetch_t *fetch;
+
+ 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);
+
+ INSIST(val->event != NULL);
+ validator_log(val, ISC_LOG_DEBUG(3), "in dlvfetched: %s",
+ dns_result_totext(eresult));
+
+ LOCK(&val->lock);
+ fetch = val->fetch;
+ val->fetch = NULL;
+ 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 = true;
+ if (dlv_algorithm_supported(val)) {
+ validator_log(val, ISC_LOG_DEBUG(3), "DLV %s found",
+ namebuf);
+ dlv_validator_start(val);
+ } else {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "DLV %s found with no supported algorithms",
+ namebuf);
+ markanswer(val, "dlvfetched (1)");
+ validator_done(val, ISC_R_SUCCESS);
+ }
+ } else if (eresult == DNS_R_NXRRSET ||
+ eresult == DNS_R_NXDOMAIN ||
+ eresult == DNS_R_NCACHENXRRSET ||
+ eresult == DNS_R_NCACHENXDOMAIN) {
+ result = finddlvsep(val, true);
+ if (result == ISC_R_SUCCESS) {
+ if (dlv_algorithm_supported(val)) {
+ 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 {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "DLV %s found with no supported "
+ "algorithms", namebuf);
+ markanswer(val, "dlvfetched (2)");
+ validator_done(val, ISC_R_SUCCESS);
+ }
+ } else if (result == ISC_R_NOTFOUND) {
+ validator_log(val, ISC_LOG_DEBUG(3), "DLV not found");
+ markanswer(val, "dlvfetched (3)");
+ 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 (fetch != NULL)
+ dns_resolver_destroyfetch(&fetch);
+ if (want_destroy)
+ destroy(val);
+}
+
+/*%
+ * Start the DLV lookup process.
+ *
+ * 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, "
+ " %s is under DLV (startfinddlvsep)", namebuf);
+ return (DNS_R_MUSTBESECURE);
+ }
+
+ val->dlvlabels = dns_name_countlabels(unsecure) - 1;
+ result = finddlvsep(val, false);
+ if (result == ISC_R_NOTFOUND) {
+ validator_log(val, ISC_LOG_DEBUG(3), "DLV not found");
+ markanswer(val, "startfinddlvsep (1)");
+ return (ISC_R_SUCCESS);
+ }
+ if (result == DNS_R_NTACOVERED) {
+ validator_log(val, ISC_LOG_DEBUG(3), "DLV covered by NTA");
+ validator_done(val, ISC_R_SUCCESS);
+ 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));
+ if (dlv_algorithm_supported(val)) {
+ validator_log(val, ISC_LOG_DEBUG(3), "DLV %s found", namebuf);
+ dlv_validator_start(val);
+ return (DNS_R_WAIT);
+ }
+ validator_log(val, ISC_LOG_DEBUG(3), "DLV %s found with no supported "
+ "algorithms", namebuf);
+ markanswer(val, "startfinddlvsep (2)");
+ validator_done(val, ISC_R_SUCCESS);
+ return (ISC_R_SUCCESS);
+}
+
+/*%
+ * 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, bool 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)) {
+ dns_name_format(val->event->name, namebuf,
+ sizeof(namebuf));
+ validator_log(val, ISC_LOG_WARNING,
+ "must be secure failure, "
+ "%s is under DLV (finddlvsep)", namebuf);
+ return (DNS_R_MUSTBESECURE);
+ }
+
+ dlvsep = dns_fixedname_initname(&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);
+ dlvname = dns_fixedname_initname(&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);
+ }
+
+ if (((val->options & DNS_VALIDATOR_NONTA) == 0) &&
+ dns_view_ntacovers(val->view, val->start, dlvname, val->view->dlv))
+ return (DNS_R_NTACOVERED);
+
+ 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 (DNS_TRUST_PENDING(val->frdataset.trust) &&
+ dns_rdataset_isassociated(&val->fsigrdataset))
+ {
+ dns_fixedname_init(&val->fname);
+ dns_name_copy(dlvname,
+ dns_fixedname_name(&val->fname),
+ NULL);
+ result = create_validator(val,
+ dns_fixedname_name(&val->fname),
+ dns_rdatatype_dlv,
+ &val->frdataset,
+ &val->fsigrdataset,
+ dlvvalidated,
+ "finddlvsep");
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ return (DNS_R_WAIT);
+ }
+ if (val->frdataset.trust < dns_trust_secure) {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "DLV not validated");
+ return (DNS_R_NOVALIDSIG);
+ }
+ val->havedlvsep = 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
+ * \li DNS_R_BROKENCHAIN
+ */
+static isc_result_t
+proveunsecure(dns_validator_t *val, bool have_ds, bool resume)
+{
+ isc_result_t result;
+ dns_fixedname_t fixedsecroot;
+ dns_name_t *secroot;
+ dns_name_t *tname;
+ char namebuf[DNS_NAME_FORMATSIZE];
+ dns_name_t *found;
+ dns_fixedname_t fixedfound;
+
+ secroot = dns_fixedname_initname(&fixedsecroot);
+ found = dns_fixedname_initname(&fixedfound);
+ if (val->havedlvsep)
+ dns_name_copy(dns_fixedname_name(&val->dlvsep), secroot, NULL);
+ else {
+ unsigned int labels;
+ 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.
+ */
+
+ labels = dns_name_countlabels(secroot);
+ if (val->event->type == dns_rdatatype_ds && labels > 1U)
+ dns_name_getlabelsequence(secroot, 1, labels - 1,
+ secroot);
+ result = dns_keytable_finddeepestmatch(val->keytable,
+ secroot, secroot);
+ if (result == ISC_R_NOTFOUND) {
+ if (val->mustbesecure) {
+ validator_log(val, ISC_LOG_WARNING,
+ "must be secure failure, "
+ "not beneath secure root");
+ result = DNS_R_MUSTBESECURE;
+ goto out;
+ } else
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "not beneath secure root");
+ if (val->view->dlv == NULL || DLVTRIED(val)) {
+ markanswer(val, "proveunsecure (1)");
+ 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 an 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', "
+ "can't fall back to DLV",
+ 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, "proveunsecure (2)");
+ result = ISC_R_SUCCESS;
+ goto out;
+ }
+ return(startfinddlvsep(val,
+ dns_fixedname_name(&val->fname)));
+ }
+ val->labels++;
+ }
+
+ for (;
+ val->labels <= dns_name_countlabels(val->event->name);
+ val->labels++)
+ {
+
+ tname = dns_fixedname_initname(&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 may be done.
+ */
+ /*
+ * If we have "trust == answer" then this namespace
+ * has switched from insecure to should be secure.
+ */
+ if (DNS_TRUST_PENDING(val->frdataset.trust) ||
+ DNS_TRUST_ANSWER(val->frdataset.trust)) {
+ result = create_validator(val, tname,
+ dns_rdatatype_ds,
+ &val->frdataset,
+ NULL, dsvalidated,
+ "proveunsecure");
+ if (result != ISC_R_SUCCESS)
+ goto out;
+ return (DNS_R_WAIT);
+ }
+ /*
+ * Zones using NSEC3 don't return a NSEC RRset so
+ * we need to use dns_view_findzonecut2 to find
+ * the zone cut.
+ */
+ if (result == DNS_R_NXRRSET &&
+ !dns_rdataset_isassociated(&val->frdataset) &&
+ dns_view_findzonecut2(val->view, tname, found,
+ 0, 0, false, false,
+ NULL, NULL) == ISC_R_SUCCESS &&
+ dns_name_equal(tname, found)) {
+ if (val->mustbesecure) {
+ validator_log(val, ISC_LOG_WARNING,
+ "must be secure failure, "
+ "no DS at zone cut");
+ return (DNS_R_MUSTBESECURE);
+ }
+ if (val->view->dlv == NULL || DLVTRIED(val)) {
+ markanswer(val, "proveunsecure (3)");
+ return (ISC_R_SUCCESS);
+ }
+ return (startfinddlvsep(val, tname));
+ }
+ 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.
+ */
+ validator_log(val, ISC_LOG_WARNING,
+ "can't validate existing "
+ "negative responses (no DS)");
+ 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, "
+ "%s is a delegation",
+ namebuf);
+ return (DNS_R_MUSTBESECURE);
+ }
+ if (val->view->dlv == NULL || DLVTRIED(val)) {
+ markanswer(val, "proveunsecure (4)");
+ return (ISC_R_SUCCESS);
+ }
+ return (startfinddlvsep(val, tname));
+ }
+ continue;
+ } else if (result == DNS_R_CNAME) {
+ if (DNS_TRUST_PENDING(val->frdataset.trust) ||
+ DNS_TRUST_ANSWER(val->frdataset.trust)) {
+ result = create_validator(val, tname,
+ dns_rdatatype_cname,
+ &val->frdataset,
+ NULL, cnamevalidated,
+ "proveunsecure "
+ "(cname)");
+ if (result != ISC_R_SUCCESS)
+ goto out;
+ return (DNS_R_WAIT);
+ }
+ 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, "
+ "no supported algorithm/"
+ "digest (%s/DS)",
+ namebuf);
+ result = DNS_R_MUSTBESECURE;
+ goto out;
+ }
+ if (val->view->dlv == NULL ||
+ DLVTRIED(val)) {
+ markanswer(val,
+ "proveunsecure (5)");
+ result = ISC_R_SUCCESS;
+ goto out;
+ }
+ return(startfinddlvsep(val, tname));
+ }
+ continue;
+ }
+ else if (!dns_rdataset_isassociated(&val->fsigrdataset))
+ {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "DS is unsigned");
+ result = DNS_R_NOVALIDSIG;
+ goto out;
+ }
+ /*
+ * Validate / re-validate answer.
+ */
+ 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 (DNS_TRUST_PENDING(val->frdataset.trust) ||
+ DNS_TRUST_ANSWER(val->frdataset.trust)) {
+ /*
+ * If we have "trust == answer" then this namespace
+ * has switched from insecure to should be secure.
+ */
+ result = create_validator(val, tname,
+ dns_rdatatype_ds,
+ &val->frdataset,
+ NULL, dsvalidated,
+ "proveunsecure");
+ if (result != ISC_R_SUCCESS)
+ goto out;
+ return (DNS_R_WAIT);
+ } 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.
+ */
+ validator_log(val, ISC_LOG_WARNING,
+ "can't validate existing "
+ "negative responses "
+ "(not a zone cut)");
+ 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);
+ } else if (result == DNS_R_BROKENCHAIN)
+ return (result);
+ }
+
+ /* Couldn't complete insecurity proof */
+ validator_log(val, ISC_LOG_DEBUG(3), "insecurity proof failed");
+ return (DNS_R_NOTINSECURE);
+
+ 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 validate 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 an unsecure positive answer.
+ */
+static void
+validator_start(isc_task_t *task, isc_event_t *event) {
+ dns_validator_t *val;
+ dns_validatorevent_t *vevent;
+ bool want_destroy = 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 canceled, 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, false, false);
+ if (result == DNS_R_NOTINSECURE)
+ result = saved_result;
+ }
+ } else if (val->event->rdataset != NULL &&
+ val->event->rdataset->type != 0) {
+ /*
+ * 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, false, false);
+ if (result == DNS_R_NOTINSECURE)
+ validator_log(val, ISC_LOG_INFO,
+ "got insecure response; "
+ "parent indicates it should be secure");
+ } 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, false);
+ } else if (val->event->rdataset != NULL &&
+ NEGATIVE(val->event->rdataset))
+ {
+ /*
+ * This is a nonexistence validation.
+ */
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "attempting negative response validation");
+
+ if (val->event->rdataset->covers == dns_rdatatype_any) {
+ val->attributes |= VALATTR_NEEDNOQNAME;
+ val->attributes |= VALATTR_NEEDNOWILDCARD;
+ } else
+ val->attributes |= VALATTR_NEEDNODATA;
+ result = nsecvalidate(val, 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 = ISC_R_FAILURE;
+ dns_validator_t *val;
+ isc_task_t *tclone = NULL;
+ dns_validatorevent_t *event;
+
+ REQUIRE(name != NULL);
+ REQUIRE(rdataset != NULL ||
+ (rdataset == NULL && sigrdataset == NULL && message != NULL));
+ REQUIRE(validatorp != NULL && *validatorp == NULL);
+
+ 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 = false;
+ event->secure = 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;
+ result = dns_view_getsecroots(val->view, &val->keytable);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_mutex;
+ 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 = false;
+ val->havedlvsep = false;
+ val->depth = 0;
+ val->authcount = 0;
+ val->authfail = 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_stdtime_get(&val->start);
+ 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_mutex:
+ DESTROYLOCK(&val->lock);
+
+ 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) {
+ dns_fetch_t *fetch = NULL;
+
+ REQUIRE(VALID_VALIDATOR(validator));
+
+ LOCK(&validator->lock);
+
+ validator_log(validator, ISC_LOG_DEBUG(3), "dns_validator_cancel");
+
+ if ((validator->attributes & VALATTR_CANCELED) == 0) {
+ validator->attributes |= VALATTR_CANCELED;
+ if (validator->event != NULL) {
+ fetch = validator->fetch;
+ validator->fetch = NULL;
+
+ if (validator->subvalidator != NULL)
+ dns_validator_cancel(validator->subvalidator);
+ if ((validator->options & DNS_VALIDATOR_DEFER) != 0) {
+ validator->options &= ~DNS_VALIDATOR_DEFER;
+ validator_done(validator, ISC_R_CANCELED);
+ }
+ }
+ }
+ UNLOCK(&validator->lock);
+
+ /* Need to cancel and destroy the fetch outside validator lock */
+ if (fetch != NULL) {
+ dns_resolver_cancelfetch(fetch);
+ dns_resolver_destroyfetch(&fetch);
+ }
+}
+
+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;
+ bool want_destroy = false;
+
+ REQUIRE(validatorp != NULL);
+ val = *validatorp;
+ REQUIRE(VALID_VALIDATOR(val));
+
+ LOCK(&val->lock);
+
+ val->attributes |= VALATTR_SHUTDOWN;
+ validator_log(val, ISC_LOG_DEBUG(4), "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 %s/%s: %s", depth, spaces,
+ namebuf, typebuf, msgbuf);
+ } else {
+ isc_log_write(dns_lctx, category, module, level,
+ "%.*svalidator @%p: %s", depth, spaces,
+ val, msgbuf);
+ }
+}
+
+static void
+validator_log(void *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..68ae7fe
--- /dev/null
+++ b/lib/dns/version.c
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <dns/version.h>
+
+const char dns_version[] = VERSION;
+const char dns_major[] = MAJOR;
+const char dns_mapapi[] = MAPAPI;
+
+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..ea6e431
--- /dev/null
+++ b/lib/dns/view.c
@@ -0,0 +1,2411 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <limits.h>
+#include <inttypes.h>
+#include <stdbool.h>
+
+#ifdef HAVE_LMDB
+ #include <lmdb.h>
+#endif
+
+#include <isc/file.h>
+#include <isc/hash.h>
+#include <isc/lex.h>
+#include <isc/print.h>
+#include <isc/sha2.h>
+#include <isc/stats.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/badcache.h>
+#include <dns/cache.h>
+#include <dns/db.h>
+#include <dns/dispatch.h>
+#include <dns/dlz.h>
+#include <dns/dns64.h>
+#include <dns/dnssec.h>
+#include <dns/events.h>
+#include <dns/forward.h>
+#include <dns/keytable.h>
+#include <dns/keyvalues.h>
+#include <dns/master.h>
+#include <dns/masterdump.h>
+#include <dns/nta.h>
+#include <dns/order.h>
+#include <dns/peer.h>
+#include <dns/rbt.h>
+#include <dns/rdataset.h>
+#include <dns/request.h>
+#include <dns/resolver.h>
+#include <dns/result.h>
+#include <dns/rpz.h>
+#include <dns/rrl.h>
+#include <dns/stats.h>
+#include <dns/time.h>
+#include <dns/tsig.h>
+#include <dns/zone.h>
+#include <dns/zt.h>
+
+#define CHECK(op) \
+ do { result = (op); \
+ if (result != ISC_R_SUCCESS) goto cleanup; \
+ } while (0)
+
+#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
+#define DNS_VIEW_FAILCACHESIZE 1021
+
+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;
+ char buffer[1024];
+
+ /*
+ * 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->nta_file = NULL;
+ view->mctx = NULL;
+ isc_mem_attach(mctx, &view->mctx);
+ view->name = isc_mem_strdup(mctx, name);
+ if (view->name == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup_view;
+ }
+
+ result = isc_file_sanitize(NULL, view->name, "nta",
+ buffer, sizeof(buffer));
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_name;
+ view->nta_file = isc_mem_strdup(mctx, buffer);
+ if (view->nta_file == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup_name;
+ }
+
+ result = isc_mutex_init(&view->lock);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_name;
+
+ view->zonetable = NULL;
+ if (isc_bind9) {
+ 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_priv = NULL;
+ view->ntatable_priv = NULL;
+ 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_zt;
+ }
+
+ view->acache = NULL;
+ view->cache = NULL;
+ view->cachedb = NULL;
+ ISC_LIST_INIT(view->dlz_searched);
+ ISC_LIST_INIT(view->dlz_unsearched);
+ view->hints = NULL;
+ view->resolver = NULL;
+ view->adb = NULL;
+ view->requestmgr = NULL;
+ view->rdclass = rdclass;
+ view->frozen = 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 = 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 = false;
+ view->rootexclude = NULL;
+ view->adbstats = NULL;
+ view->resstats = NULL;
+ view->resquerystats = NULL;
+ view->cacheshared = false;
+ ISC_LIST_INIT(view->dns64);
+ view->dns64cnt = 0;
+
+ /*
+ * Initialize configuration data with default values.
+ */
+ view->recursion = true;
+ view->auth_nxdomain = false; /* Was true in BIND 8 */
+ view->additionalfromcache = true;
+ view->additionalfromauth = true;
+ view->enablednssec = true;
+ view->enablevalidation = true;
+ view->acceptexpired = false;
+ view->minimal_any = false;
+ view->minimalresponses = dns_minimal_no;
+ view->transfer_format = dns_one_answer;
+ view->cacheacl = NULL;
+ view->cacheonacl = NULL;
+ 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->denyansweracl = NULL;
+ view->nocasecompress = NULL;
+ view->msgcompression = true;
+ view->answeracl_exclude = NULL;
+ view->denyanswernames = NULL;
+ view->answernames_exclude = NULL;
+ view->rrl = NULL;
+ view->provideixfr = true;
+ view->maxcachettl = 7 * 24 * 3600;
+ view->maxncachettl = 3 * 3600;
+ view->nta_lifetime = 0;
+ view->nta_recheck = 0;
+ view->prefetch_eligible = 0;
+ view->prefetch_trigger = 0;
+ view->dstport = 53;
+ view->preferred_glue = 0;
+ view->flush = false;
+ view->dlv = NULL;
+ view->maxudp = 0;
+ view->nocookieudp = 0;
+ view->maxbits = 0;
+ view->v4_aaaa = dns_aaaa_ok;
+ view->v6_aaaa = dns_aaaa_ok;
+ view->aaaa_acl = NULL;
+ view->rpzs = NULL;
+ view->catzs = NULL;
+ dns_fixedname_init(&view->dlv_fixed);
+ view->managed_keys = NULL;
+ view->redirect = NULL;
+ view->redirectzone = NULL;
+ dns_fixedname_init(&view->redirectfixed);
+ view->requestnsid = false;
+ view->sendcookie = true;
+ view->requireservercookie = false;
+ view->trust_anchor_telemetry = true;
+ view->root_key_sentinel = true;
+ view->new_zone_file = NULL;
+ view->new_zone_db = NULL;
+ view->new_zone_dbenv = NULL;
+ view->new_zone_mapsize = 0ULL;
+ view->new_zone_config = NULL;
+ view->cfg_destroy = NULL;
+ view->fail_ttl = 0;
+ view->failcache = NULL;
+ (void)dns_badcache_init(view->mctx, DNS_VIEW_FAILCACHESIZE,
+ &view->failcache);
+ view->v6bias = 0;
+ view->dtenv = NULL;
+ view->dttypes = 0;
+
+ result = isc_mutex_init(&view->new_zone_lock);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_dynkeys;
+
+ if (isc_bind9) {
+ result = dns_order_create(view->mctx, &view->order);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_new_zone_lock;
+ }
+
+ 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->viewlist = NULL;
+ view->magic = DNS_VIEW_MAGIC;
+
+ *viewp = view;
+
+ return (ISC_R_SUCCESS);
+
+ cleanup_peerlist:
+ if (view->peers != NULL)
+ dns_peerlist_detach(&view->peers);
+
+ cleanup_order:
+ if (view->order != NULL)
+ dns_order_detach(&view->order);
+
+ cleanup_new_zone_lock:
+ DESTROYLOCK(&view->new_zone_lock);
+
+ cleanup_dynkeys:
+ if (view->dynamickeys != NULL)
+ dns_tsigkeyring_detach(&view->dynamickeys);
+
+ cleanup_references:
+ isc_refcount_decrement(&view->references, NULL);
+ isc_refcount_destroy(&view->references);
+
+ cleanup_fwdtable:
+ if (view->fwdtable != NULL)
+ dns_fwdtable_destroy(&view->fwdtable);
+
+ cleanup_zt:
+ if (view->zonetable != NULL)
+ dns_zt_detach(&view->zonetable);
+
+ cleanup_mutex:
+ DESTROYLOCK(&view->lock);
+
+ if (view->nta_file != NULL)
+ isc_mem_free(mctx, view->nta_file);
+
+ cleanup_name:
+ isc_mem_free(mctx, view->name);
+
+ cleanup_view:
+ isc_mem_putanddetach(&view->mctx, view, sizeof(*view));
+
+ return (result);
+}
+
+static inline void
+destroy(dns_view_t *view) {
+ dns_dns64_t *dns64;
+ dns_dlzdb_t *dlzdb;
+
+ 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) {
+ isc_result_t result;
+ char template[PATH_MAX];
+ char keyfile[PATH_MAX];
+ FILE *fp = NULL;
+
+ result = isc_file_mktemplate(NULL, template, sizeof(template));
+ if (result == ISC_R_SUCCESS) {
+ (void)isc_file_openuniqueprivate(template, &fp);
+ }
+ if (fp == NULL) {
+ dns_tsigkeyring_detach(&view->dynamickeys);
+ } else {
+ result = dns_tsigkeyring_dumpanddetach
+ (&view->dynamickeys, fp);
+ if (result == ISC_R_SUCCESS) {
+ if (fclose(fp) == 0) {
+ result = isc_file_sanitize
+ (NULL, view->name, "tsigkeys",
+ keyfile, sizeof(keyfile));
+ if (result == ISC_R_SUCCESS)
+ result = isc_file_rename
+ (template, keyfile);
+ }
+ if (result != ISC_R_SUCCESS)
+ (void)remove(template);
+ } else {
+ (void)fclose(fp);
+ (void)remove(template);
+ }
+ }
+ }
+ if (view->statickeys != NULL)
+ dns_tsigkeyring_detach(&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);
+ }
+ dns_rrl_view_destroy(view);
+ if (view->rpzs != NULL)
+ dns_rpz_detach_rpzs(&view->rpzs);
+ if (view->catzs != NULL)
+ dns_catz_catzs_detach(&view->catzs);
+ for (dlzdb = ISC_LIST_HEAD(view->dlz_searched);
+ dlzdb != NULL;
+ dlzdb = ISC_LIST_HEAD(view->dlz_searched)) {
+ ISC_LIST_UNLINK(view->dlz_searched, dlzdb, link);
+ dns_dlzdestroy(&dlzdb);
+ }
+ for (dlzdb = ISC_LIST_HEAD(view->dlz_unsearched);
+ dlzdb != NULL;
+ dlzdb = ISC_LIST_HEAD(view->dlz_unsearched)) {
+ ISC_LIST_UNLINK(view->dlz_unsearched, dlzdb, link);
+ dns_dlzdestroy(&dlzdb);
+ }
+ 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->cachedb != NULL)
+ dns_db_detach(&view->cachedb);
+ if (view->cache != NULL)
+ dns_cache_detach(&view->cache);
+ if (view->nocasecompress != NULL)
+ dns_acl_detach(&view->nocasecompress);
+ if (view->matchclients != NULL)
+ dns_acl_detach(&view->matchclients);
+ if (view->matchdestinations != NULL)
+ dns_acl_detach(&view->matchdestinations);
+ if (view->cacheacl != NULL)
+ dns_acl_detach(&view->cacheacl);
+ if (view->cacheonacl != NULL)
+ dns_acl_detach(&view->cacheonacl);
+ 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->denyansweracl != NULL)
+ dns_acl_detach(&view->denyansweracl);
+ if (view->aaaa_acl != NULL)
+ dns_acl_detach(&view->aaaa_acl);
+ if (view->answeracl_exclude != NULL)
+ dns_rbt_destroy(&view->answeracl_exclude);
+ if (view->denyanswernames != NULL)
+ dns_rbt_destroy(&view->denyanswernames);
+ if (view->answernames_exclude != NULL)
+ dns_rbt_destroy(&view->answernames_exclude);
+ 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->adbstats != NULL)
+ isc_stats_detach(&view->adbstats);
+ if (view->resstats != NULL)
+ isc_stats_detach(&view->resstats);
+ if (view->resquerystats != NULL)
+ dns_stats_detach(&view->resquerystats);
+ if (view->secroots_priv != NULL)
+ dns_keytable_detach(&view->secroots_priv);
+ if (view->ntatable_priv != NULL)
+ dns_ntatable_detach(&view->ntatable_priv);
+ for (dns64 = ISC_LIST_HEAD(view->dns64);
+ dns64 != NULL;
+ dns64 = ISC_LIST_HEAD(view->dns64)) {
+ dns_dns64_unlink(&view->dns64, dns64);
+ dns_dns64_destroy(&dns64);
+ }
+ if (view->managed_keys != NULL)
+ dns_zone_detach(&view->managed_keys);
+ if (view->redirect != NULL)
+ dns_zone_detach(&view->redirect);
+#ifdef HAVE_DNSTAP
+ if (view->dtenv != NULL)
+ dns_dt_detach(&view->dtenv);
+#endif /* HAVE_DNSTAP */
+ dns_view_setnewzones(view, false, NULL, NULL, 0ULL);
+ if (view->new_zone_file != NULL) {
+ isc_mem_free(view->mctx, view->new_zone_file);
+ view->new_zone_file = NULL;
+ }
+#ifdef HAVE_LMDB
+ if (view->new_zone_dbenv != NULL) {
+ mdb_env_close((MDB_env *) view->new_zone_dbenv);
+ view->new_zone_dbenv = NULL;
+ }
+ if (view->new_zone_db != NULL) {
+ isc_mem_free(view->mctx, view->new_zone_db);
+ view->new_zone_db = NULL;
+ }
+#endif /* HAVE_LMDB */
+ dns_fwdtable_destroy(&view->fwdtable);
+ dns_aclenv_destroy(&view->aclenv);
+ if (view->failcache != NULL)
+ dns_badcache_destroy(&view->failcache);
+ DESTROYLOCK(&view->new_zone_lock);
+ DESTROYLOCK(&view->lock);
+ isc_refcount_destroy(&view->references);
+ isc_mem_free(view->mctx, view->nta_file);
+ isc_mem_free(view->mctx, view->name);
+ isc_mem_putanddetach(&view->mctx, view, sizeof(*view));
+}
+
+/*
+ * Return true iff 'view' may be freed.
+ * The caller must be holding the view lock.
+ */
+static bool
+all_done(dns_view_t *view) {
+
+ if (isc_refcount_current(&view->references) == 0 &&
+ view->weakrefs == 0 &&
+ RESSHUTDOWN(view) && ADBSHUTDOWN(view) && REQSHUTDOWN(view))
+ return (true);
+
+ return (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, bool flush) {
+ dns_view_t *view;
+ unsigned int refs;
+ bool done = false;
+
+ REQUIRE(viewp != NULL);
+ view = *viewp;
+ REQUIRE(DNS_VIEW_VALID(view));
+
+ if (flush)
+ view->flush = true;
+ isc_refcount_decrement(&view->references, &refs);
+ if (refs == 0) {
+ dns_zone_t *mkzone = NULL, *rdzone = NULL;
+
+ 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->zonetable != NULL) {
+ if (view->flush)
+ dns_zt_flushanddetach(&view->zonetable);
+ else
+ dns_zt_detach(&view->zonetable);
+ }
+ if (view->managed_keys != NULL) {
+ mkzone = view->managed_keys;
+ view->managed_keys = NULL;
+ if (view->flush)
+ dns_zone_flush(mkzone);
+ }
+ if (view->redirect != NULL) {
+ rdzone = view->redirect;
+ view->redirect = NULL;
+ if (view->flush)
+ dns_zone_flush(rdzone);
+ }
+ if (view->catzs != NULL) {
+ dns_catz_catzs_detach(&view->catzs);
+ }
+ done = all_done(view);
+ UNLOCK(&view->lock);
+
+ /* Need to detach zones outside view lock */
+ if (mkzone != NULL)
+ dns_zone_detach(&mkzone);
+
+ if (rdzone != NULL)
+ dns_zone_detach(&rdzone);
+ }
+
+ *viewp = NULL;
+
+ if (done)
+ destroy(view);
+}
+
+void
+dns_view_flushanddetach(dns_view_t **viewp) {
+ view_flushanddetach(viewp, true);
+}
+
+void
+dns_view_detach(dns_view_t **viewp) {
+ view_flushanddetach(viewp, 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));
+ REQUIRE(view->zonetable != NULL);
+
+ (void)dns_zt_apply(view->zonetable, 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;
+ bool done = 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;
+ bool done;
+
+ REQUIRE(event->ev_type == DNS_EVENT_VIEWRESSHUTDOWN);
+ REQUIRE(DNS_VIEW_VALID(view));
+ REQUIRE(view->task == task);
+
+ UNUSED(task);
+
+ isc_event_free(&event);
+
+ LOCK(&view->lock);
+
+ view->attributes |= DNS_VIEWATTR_RESSHUTDOWN;
+ done = all_done(view);
+
+ UNLOCK(&view->lock);
+
+ if (done)
+ destroy(view);
+}
+
+static void
+adb_shutdown(isc_task_t *task, isc_event_t *event) {
+ dns_view_t *view = event->ev_arg;
+ bool done;
+
+ REQUIRE(event->ev_type == DNS_EVENT_VIEWADBSHUTDOWN);
+ REQUIRE(DNS_VIEW_VALID(view));
+ REQUIRE(view->task == task);
+
+ UNUSED(task);
+
+ isc_event_free(&event);
+
+ LOCK(&view->lock);
+
+ view->attributes |= DNS_VIEWATTR_ADBSHUTDOWN;
+ done = all_done(view);
+
+ UNLOCK(&view->lock);
+
+ if (done)
+ destroy(view);
+}
+
+static void
+req_shutdown(isc_task_t *task, isc_event_t *event) {
+ dns_view_t *view = event->ev_arg;
+ bool done;
+
+ REQUIRE(event->ev_type == DNS_EVENT_VIEWREQSHUTDOWN);
+ REQUIRE(DNS_VIEW_VALID(view));
+ REQUIRE(view->task == task);
+
+ UNUSED(task);
+
+ isc_event_free(&event);
+
+ LOCK(&view->lock);
+
+ view->attributes |= DNS_VIEWATTR_REQSHUTDOWN;
+ done = all_done(view);
+
+ UNLOCK(&view->lock);
+
+ if (done)
+ destroy(view);
+}
+
+isc_result_t
+dns_view_createzonetable(dns_view_t *view) {
+
+ REQUIRE(DNS_VIEW_VALID(view));
+ REQUIRE(!view->frozen);
+ REQUIRE(view->zonetable == NULL);
+
+ return (dns_zt_create(view->mctx, view->rdclass, &view->zonetable));
+}
+
+isc_result_t
+dns_view_createresolver(dns_view_t *view,
+ isc_taskmgr_t *taskmgr,
+ unsigned int ntasks,
+ unsigned int ndisp,
+ 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, ndisp, 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),
+ dispatchv4, dispatchv6,
+ &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) {
+ dns_view_setcache2(view, cache, false);
+}
+
+void
+dns_view_setcache2(dns_view_t *view, dns_cache_t *cache, bool shared) {
+ REQUIRE(DNS_VIEW_VALID(view));
+ REQUIRE(!view->frozen);
+
+ view->cacheshared = shared;
+ 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);
+}
+
+bool
+dns_view_iscacheshared(dns_view_t *view) {
+ REQUIRE(DNS_VIEW_VALID(view));
+
+ return (view->cacheshared);
+}
+
+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_detach(&view->statickeys);
+ dns_tsigkeyring_attach(ring, &view->statickeys);
+}
+
+void
+dns_view_setdynamickeyring(dns_view_t *view, dns_tsig_keyring_t *ring) {
+ REQUIRE(DNS_VIEW_VALID(view));
+ REQUIRE(ring != NULL);
+ if (view->dynamickeys != NULL)
+ dns_tsigkeyring_detach(&view->dynamickeys);
+ dns_tsigkeyring_attach(ring, &view->dynamickeys);
+}
+
+void
+dns_view_getdynamickeyring(dns_view_t *view, dns_tsig_keyring_t **ringp) {
+ REQUIRE(DNS_VIEW_VALID(view));
+ REQUIRE(ringp != NULL && *ringp == NULL);
+ if (view->dynamickeys != NULL)
+ dns_tsigkeyring_attach(view->dynamickeys, ringp);
+}
+
+void
+dns_view_restorekeyring(dns_view_t *view) {
+ FILE *fp;
+ char keyfile[PATH_MAX];
+ isc_result_t result;
+
+ REQUIRE(DNS_VIEW_VALID(view));
+
+ if (view->dynamickeys != NULL) {
+ result = isc_file_sanitize(NULL, view->name, "tsigkeys",
+ keyfile, sizeof(keyfile));
+ if (result == ISC_R_SUCCESS) {
+ fp = fopen(keyfile, "r");
+ if (fp != NULL) {
+ dns_keyring_restore(view->dynamickeys, fp);
+ (void)fclose(fp);
+ }
+ }
+ }
+}
+
+void
+dns_view_setdstport(dns_view_t *view, in_port_t dstport) {
+ REQUIRE(DNS_VIEW_VALID(view));
+ view->dstport = dstport;
+}
+
+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 = true;
+}
+
+void
+dns_view_thaw(dns_view_t *view) {
+ REQUIRE(DNS_VIEW_VALID(view));
+ REQUIRE(view->frozen);
+
+ view->frozen = false;
+}
+
+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);
+ REQUIRE(view->zonetable != NULL);
+
+ result = dns_zt_mount(view->zonetable, zone);
+
+ return (result);
+}
+
+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));
+
+ LOCK(&view->lock);
+ if (view->zonetable != NULL) {
+ result = dns_zt_find(view->zonetable, name, 0, NULL, zonep);
+ if (result == DNS_R_PARTIALMATCH) {
+ dns_zone_detach(zonep);
+ result = ISC_R_NOTFOUND;
+ }
+ } else
+ result = ISC_R_NOTFOUND;
+ UNLOCK(&view->lock);
+
+ 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, bool use_hints,
+ dns_db_t **dbp, dns_dbnode_t **nodep, dns_name_t *foundname,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) {
+ return (dns_view_find2(view, name, type, now, options, use_hints,
+ false, dbp, nodep, foundname, rdataset,
+ sigrdataset));
+}
+
+isc_result_t
+dns_view_find2(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type,
+ isc_stdtime_t now, unsigned int options,
+ bool use_hints, bool use_static_stub,
+ 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;
+ bool is_cache, is_staticstub_zone;
+ 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.
+ */
+ db = NULL;
+ node = NULL;
+ is_staticstub_zone = false;
+ zone = NULL;
+ LOCK(&view->lock);
+ if (view->zonetable != NULL)
+ result = dns_zt_find(view->zonetable, name, 0, NULL, &zone);
+ else
+ result = ISC_R_NOTFOUND;
+ UNLOCK(&view->lock);
+ if (zone != NULL && dns_zone_gettype(zone) == dns_zone_staticstub &&
+ !use_static_stub)
+ result = ISC_R_NOTFOUND;
+ 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;
+ if (dns_zone_gettype(zone) == dns_zone_staticstub &&
+ dns_name_equal(name, dns_zone_getorigin(zone))) {
+ is_staticstub_zone = true;
+ }
+ } 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 && !is_staticstub_zone) {
+ /*
+ * Either the answer is in the cache, or we
+ * don't know it.
+ * Note that if the result comes from a
+ * static-stub zone we stop the search here
+ * (see the function description in view.h).
+ */
+ is_cache = 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 && !is_staticstub_zone) {
+ /*
+ * We found an answer, but the cache may be better.
+ * Remember what we've got and go look in the cache.
+ */
+ is_cache = 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,
+ bool 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,
+ bool use_hints,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
+{
+ return(dns_view_findzonecut2(view, name, fname, now, options,
+ use_hints, 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,
+ bool use_hints, bool use_cache,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
+{
+ isc_result_t result;
+ dns_db_t *db;
+ bool is_cache, use_zone, try_hints;
+ dns_zone_t *zone;
+ dns_name_t *zfname;
+ dns_rdataset_t zrdataset, zsigrdataset;
+ dns_fixedname_t zfixedname;
+ unsigned int ztoptions = 0;
+
+ REQUIRE(DNS_VIEW_VALID(view));
+ REQUIRE(view->frozen);
+
+ db = NULL;
+ use_zone = false;
+ try_hints = false;
+ zfname = NULL;
+
+ /*
+ * Initialize.
+ */
+ dns_fixedname_init(&zfixedname);
+ dns_rdataset_init(&zrdataset);
+ dns_rdataset_init(&zsigrdataset);
+
+ /*
+ * Find the right database.
+ */
+ zone = NULL;
+ LOCK(&view->lock);
+ if (view->zonetable != NULL) {
+ if ((options & DNS_DBFIND_NOEXACT) != 0)
+ ztoptions |= DNS_ZTFIND_NOEXACT;
+ result = dns_zt_find(view->zonetable, name, ztoptions,
+ NULL, &zone);
+ } else
+ result = ISC_R_NOTFOUND;
+ UNLOCK(&view->lock);
+ 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 = 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 = 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) ||
+ (dns_zone_gettype(zone) == dns_zone_staticstub &&
+ dns_name_equal(fname, zfname)))) {
+ /*
+ * We found a zonecut in the cache, but our
+ * zone delegation is better.
+ */
+ use_zone = 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 = true;
+ } else {
+ /*
+ * Maybe we have hints...
+ */
+ try_hints = 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,
+ bool 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);
+ REQUIRE(zonep != NULL && *zonep == NULL);
+
+ for (view = ISC_LIST_HEAD(*list);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link)) {
+ if (allclasses == 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;
+ LOCK(&view->lock);
+ if (view->zonetable != NULL)
+ result = dns_zt_find(view->zonetable, name, 0,
+ NULL, zp);
+ else
+ result = ISC_R_NOTFOUND;
+ UNLOCK(&view->lock);
+ 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;
+ POST(result);
+ }
+
+ if (zone2 != NULL) {
+ dns_zone_detach(&zone1);
+ dns_zone_detach(&zone2);
+ return (ISC_R_MULTIPLE);
+ }
+ }
+
+ 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, bool stop) {
+
+ REQUIRE(DNS_VIEW_VALID(view));
+ REQUIRE(view->zonetable != NULL);
+
+ return (dns_zt_load(view->zonetable, stop));
+}
+
+isc_result_t
+dns_view_loadnew(dns_view_t *view, bool stop) {
+
+ REQUIRE(DNS_VIEW_VALID(view));
+ REQUIRE(view->zonetable != NULL);
+
+ return (dns_zt_loadnew(view->zonetable, stop));
+}
+
+isc_result_t
+dns_view_asyncload(dns_view_t *view, dns_zt_allloaded_t callback, void *arg) {
+ return (dns_view_asyncload2(view, callback, arg, false));
+}
+
+isc_result_t
+dns_view_asyncload2(dns_view_t *view, dns_zt_allloaded_t callback, void *arg,
+ bool newonly) {
+ REQUIRE(DNS_VIEW_VALID(view));
+ REQUIRE(view->zonetable != NULL);
+
+ return (dns_zt_asyncload2(view->zonetable, callback, arg, newonly));
+}
+
+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);
+
+ result = dns_view_gettsig(view, keyname, keyp);
+ return ((result == ISC_R_NOTFOUND) ? ISC_R_FAILURE : result);
+}
+
+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);
+ dns_resolver_printbadcache(view->resolver, fp);
+ dns_badcache_print(view->failcache, "SERVFAIL cache", fp);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_view_flushcache(dns_view_t *view) {
+ return (dns_view_flushcache2(view, false));
+}
+
+isc_result_t
+dns_view_flushcache2(dns_view_t *view, bool fixuponly) {
+ isc_result_t result;
+
+ REQUIRE(DNS_VIEW_VALID(view));
+
+ if (view->cachedb == NULL)
+ return (ISC_R_SUCCESS);
+ if (!fixuponly) {
+ 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);
+ if (view->resolver != NULL)
+ dns_resolver_flushbadcache(view->resolver, NULL);
+ if (view->failcache != NULL)
+ dns_badcache_flush(view->failcache);
+
+ dns_adb_flush(view->adb);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_view_flushname(dns_view_t *view, dns_name_t *name) {
+ return (dns_view_flushnode(view, name, false));
+}
+
+isc_result_t
+dns_view_flushnode(dns_view_t *view, dns_name_t *name, bool tree) {
+ isc_result_t result = ISC_R_SUCCESS;
+
+ REQUIRE(DNS_VIEW_VALID(view));
+
+ if (tree) {
+ if (view->adb != NULL)
+ dns_adb_flushnames(view->adb, name);
+ if (view->resolver != NULL)
+ dns_resolver_flushbadnames(view->resolver, name);
+ if (view->failcache != NULL)
+ dns_badcache_flushtree(view->failcache, name);
+ } else {
+ if (view->adb != NULL)
+ dns_adb_flushname(view->adb, name);
+ if (view->resolver != NULL)
+ dns_resolver_flushbadcache(view->resolver, name);
+ if (view->failcache != NULL)
+ dns_badcache_flushname(view->failcache, name);
+ }
+
+ if (view->cache != NULL)
+ result = dns_cache_flushnode(view->cache, name, tree);
+
+ return (result);
+}
+
+isc_result_t
+dns_view_adddelegationonly(dns_view_t *view, dns_name_t *name) {
+ isc_result_t result;
+ dns_name_t *item;
+ 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, false) % DNS_VIEW_DELONLYHASH;
+ item = ISC_LIST_HEAD(view->delonly[hash]);
+ while (item != NULL && !dns_name_equal(item, name))
+ item = ISC_LIST_NEXT(item, link);
+ if (item != NULL)
+ return (ISC_R_SUCCESS);
+ item = isc_mem_get(view->mctx, sizeof(*item));
+ if (item == NULL)
+ return (ISC_R_NOMEMORY);
+ dns_name_init(item, NULL);
+ result = dns_name_dup(name, view->mctx, item);
+ if (result == ISC_R_SUCCESS)
+ ISC_LIST_APPEND(view->delonly[hash], item, link);
+ else
+ isc_mem_put(view->mctx, item, sizeof(*item));
+ return (result);
+}
+
+isc_result_t
+dns_view_excludedelegationonly(dns_view_t *view, dns_name_t *name) {
+ isc_result_t result;
+ dns_name_t *item;
+ 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, false) % DNS_VIEW_DELONLYHASH;
+ item = ISC_LIST_HEAD(view->rootexclude[hash]);
+ while (item != NULL && !dns_name_equal(item, name))
+ item = ISC_LIST_NEXT(item, link);
+ if (item != NULL)
+ return (ISC_R_SUCCESS);
+ item = isc_mem_get(view->mctx, sizeof(*item));
+ if (item == NULL)
+ return (ISC_R_NOMEMORY);
+ dns_name_init(item, NULL);
+ result = dns_name_dup(name, view->mctx, item);
+ if (result == ISC_R_SUCCESS)
+ ISC_LIST_APPEND(view->rootexclude[hash], item, link);
+ else
+ isc_mem_put(view->mctx, item, sizeof(*item));
+ return (result);
+}
+
+bool
+dns_view_isdelegationonly(dns_view_t *view, dns_name_t *name) {
+ dns_name_t *item;
+ uint32_t hash;
+
+ REQUIRE(DNS_VIEW_VALID(view));
+
+ if (!view->rootdelonly && view->delonly == NULL)
+ return (false);
+
+ hash = dns_name_hash(name, false) % DNS_VIEW_DELONLYHASH;
+ if (view->rootdelonly && dns_name_countlabels(name) <= 2) {
+ if (view->rootexclude == NULL)
+ return (true);
+ item = ISC_LIST_HEAD(view->rootexclude[hash]);
+ while (item != NULL && !dns_name_equal(item, name))
+ item = ISC_LIST_NEXT(item, link);
+ if (item == NULL)
+ return (true);
+ }
+
+ if (view->delonly == NULL)
+ return (false);
+
+ item = ISC_LIST_HEAD(view->delonly[hash]);
+ while (item != NULL && !dns_name_equal(item, name))
+ item = ISC_LIST_NEXT(item, link);
+ if (item == NULL)
+ return (false);
+ return (true);
+}
+
+void
+dns_view_setrootdelonly(dns_view_t *view, bool value) {
+ REQUIRE(DNS_VIEW_VALID(view));
+ view->rootdelonly = value;
+}
+
+bool
+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, bool value) {
+
+ REQUIRE(DNS_VIEW_VALID(view));
+ REQUIRE(view->zonetable != NULL);
+
+ return (dns_zt_freezezones(view->zonetable, value));
+}
+
+void
+dns_view_setadbstats(dns_view_t *view, isc_stats_t *stats) {
+ REQUIRE(DNS_VIEW_VALID(view));
+ REQUIRE(!view->frozen);
+ REQUIRE(view->adbstats == NULL);
+
+ isc_stats_attach(stats, &view->adbstats);
+}
+
+void
+dns_view_getadbstats(dns_view_t *view, isc_stats_t **statsp) {
+ REQUIRE(DNS_VIEW_VALID(view));
+ REQUIRE(statsp != NULL && *statsp == NULL);
+
+ if (view->adbstats != NULL)
+ isc_stats_attach(view->adbstats, statsp);
+}
+
+void
+dns_view_setresstats(dns_view_t *view, isc_stats_t *stats) {
+
+ REQUIRE(DNS_VIEW_VALID(view));
+ REQUIRE(!view->frozen);
+ REQUIRE(view->resstats == NULL);
+
+ isc_stats_attach(stats, &view->resstats);
+}
+
+void
+dns_view_getresstats(dns_view_t *view, isc_stats_t **statsp) {
+ REQUIRE(DNS_VIEW_VALID(view));
+ REQUIRE(statsp != NULL && *statsp == NULL);
+
+ if (view->resstats != NULL)
+ isc_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);
+}
+
+isc_result_t
+dns_view_initntatable(dns_view_t *view,
+ isc_taskmgr_t *taskmgr, isc_timermgr_t *timermgr)
+{
+ REQUIRE(DNS_VIEW_VALID(view));
+ if (view->ntatable_priv != NULL)
+ dns_ntatable_detach(&view->ntatable_priv);
+ return (dns_ntatable_create(view, taskmgr, timermgr,
+ &view->ntatable_priv));
+}
+
+isc_result_t
+dns_view_getntatable(dns_view_t *view, dns_ntatable_t **ntp) {
+ REQUIRE(DNS_VIEW_VALID(view));
+ REQUIRE(ntp != NULL && *ntp == NULL);
+ if (view->ntatable_priv == NULL)
+ return (ISC_R_NOTFOUND);
+ dns_ntatable_attach(view->ntatable_priv, ntp);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_view_initsecroots(dns_view_t *view, isc_mem_t *mctx) {
+ REQUIRE(DNS_VIEW_VALID(view));
+ if (view->secroots_priv != NULL)
+ dns_keytable_detach(&view->secroots_priv);
+ return (dns_keytable_create(mctx, &view->secroots_priv));
+}
+
+isc_result_t
+dns_view_getsecroots(dns_view_t *view, dns_keytable_t **ktp) {
+ REQUIRE(DNS_VIEW_VALID(view));
+ REQUIRE(ktp != NULL && *ktp == NULL);
+ if (view->secroots_priv == NULL)
+ return (ISC_R_NOTFOUND);
+ dns_keytable_attach(view->secroots_priv, ktp);
+ return (ISC_R_SUCCESS);
+}
+
+bool
+dns_view_ntacovers(dns_view_t *view, isc_stdtime_t now,
+ dns_name_t *name, dns_name_t *anchor)
+{
+ REQUIRE(DNS_VIEW_VALID(view));
+
+ if (view->ntatable_priv == NULL)
+ return (false);
+
+ return (dns_ntatable_covered(view->ntatable_priv, now, name, anchor));
+}
+
+isc_result_t
+dns_view_issecuredomain(dns_view_t *view, dns_name_t *name,
+ isc_stdtime_t now, bool checknta,
+ bool *secure_domain)
+{
+ isc_result_t result;
+ bool secure = false;
+ dns_fixedname_t fn;
+ dns_name_t *anchor;
+
+ REQUIRE(DNS_VIEW_VALID(view));
+
+ if (view->secroots_priv == NULL)
+ return (ISC_R_NOTFOUND);
+
+ anchor = dns_fixedname_initname(&fn);
+
+ result = dns_keytable_issecuredomain(view->secroots_priv, name,
+ anchor, &secure);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ if (checknta && secure && view->ntatable_priv != NULL &&
+ dns_ntatable_covered(view->ntatable_priv, now, name, anchor))
+ secure = false;
+
+ *secure_domain = secure;
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_view_untrust(dns_view_t *view, dns_name_t *keyname,
+ dns_rdata_dnskey_t *dnskey, isc_mem_t *mctx)
+{
+ isc_result_t result;
+ unsigned char data[4096];
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_buffer_t buffer;
+ dst_key_t *key = NULL;
+ dns_keytable_t *sr = NULL;
+
+ /*
+ * Clear the revoke bit, if set, so that the key will match what's
+ * in secroots now.
+ */
+ dnskey->flags &= ~DNS_KEYFLAG_REVOKE;
+
+ /* Convert dnskey to DST key. */
+ isc_buffer_init(&buffer, data, sizeof(data));
+ dns_rdata_fromstruct(&rdata, dnskey->common.rdclass,
+ dns_rdatatype_dnskey, dnskey, &buffer);
+
+ result = dns_dnssec_keyfromrdata(keyname, &rdata, mctx, &key);
+ if (result != ISC_R_SUCCESS)
+ return;
+
+ result = dns_view_getsecroots(view, &sr);
+ if (result == ISC_R_SUCCESS) {
+ result = dns_keytable_deletekeynode(sr, key);
+
+ /*
+ * If key was found in secroots, then it was a
+ * configured trust anchor, and we want to fail
+ * secure. If there are no other configured keys,
+ * then leave a null key so that we can't validate
+ * anymore.
+ */
+
+ if (result == ISC_R_SUCCESS)
+ dns_keytable_marksecure(sr, keyname);
+
+ dns_keytable_detach(&sr);
+ }
+
+ dst_key_free(&key);
+}
+
+isc_result_t
+dns_view_setnewzones(dns_view_t *view, bool allow, void *cfgctx,
+ void (*cfg_destroy)(void **), uint64_t mapsize)
+{
+ isc_result_t result = ISC_R_SUCCESS;
+ char buffer[1024];
+#ifdef HAVE_LMDB
+ MDB_env *env = NULL;
+ int status;
+#endif
+
+#ifndef HAVE_LMDB
+ UNUSED(mapsize);
+#endif
+
+ REQUIRE(DNS_VIEW_VALID(view));
+ REQUIRE((cfgctx != NULL && cfg_destroy != NULL) || !allow);
+
+ if (view->new_zone_file != NULL) {
+ isc_mem_free(view->mctx, view->new_zone_file);
+ view->new_zone_file = NULL;
+ }
+
+#ifdef HAVE_LMDB
+ if (view->new_zone_dbenv != NULL) {
+ mdb_env_close((MDB_env *) view->new_zone_dbenv);
+ view->new_zone_dbenv = NULL;
+ }
+
+ if (view->new_zone_db != NULL) {
+ isc_mem_free(view->mctx, view->new_zone_db);
+ view->new_zone_db = NULL;
+ }
+#endif /* HAVE_LMDB */
+
+ if (view->new_zone_config != NULL) {
+ view->cfg_destroy(&view->new_zone_config);
+ view->cfg_destroy = NULL;
+ }
+
+ if (!allow)
+ return (ISC_R_SUCCESS);
+
+ CHECK(isc_file_sanitize(NULL, view->name, "nzf",
+ buffer, sizeof(buffer)));
+
+ view->new_zone_file = isc_mem_strdup(view->mctx, buffer);
+ if (view->new_zone_file == NULL) {
+ CHECK(ISC_R_NOMEMORY);
+ }
+
+#ifdef HAVE_LMDB
+ CHECK(isc_file_sanitize(NULL, view->name, "nzd",
+ buffer, sizeof(buffer)));
+
+ view->new_zone_db = isc_mem_strdup(view->mctx, buffer);
+ if (view->new_zone_db == NULL) {
+ CHECK(ISC_R_NOMEMORY);
+ }
+
+ status = mdb_env_create(&env);
+ if (status != MDB_SUCCESS) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ ISC_LOGMODULE_OTHER, ISC_LOG_ERROR,
+ "mdb_env_create failed: %s",
+ mdb_strerror(status));
+ CHECK(ISC_R_FAILURE);
+ }
+
+ if (mapsize != 0ULL) {
+ status = mdb_env_set_mapsize(env, mapsize);
+ if (status != MDB_SUCCESS) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ ISC_LOGMODULE_OTHER, ISC_LOG_ERROR,
+ "mdb_env_set_mapsize failed: %s",
+ mdb_strerror(status));
+ CHECK(ISC_R_FAILURE);
+ }
+ view->new_zone_mapsize = mapsize;
+ }
+
+ status = mdb_env_open(env, view->new_zone_db, DNS_LMDB_FLAGS, 0600);
+ if (status != MDB_SUCCESS) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ ISC_LOGMODULE_OTHER, ISC_LOG_ERROR,
+ "mdb_env_open of '%s' failed: %s",
+ view->new_zone_db, mdb_strerror(status));
+ CHECK(ISC_R_FAILURE);
+ }
+
+ view->new_zone_dbenv = env;
+ env = NULL;
+#endif /* HAVE_LMDB */
+
+ view->new_zone_config = cfgctx;
+ view->cfg_destroy = cfg_destroy;
+
+ cleanup:
+ if (result != ISC_R_SUCCESS) {
+ if (view->new_zone_file != NULL) {
+ isc_mem_free(view->mctx, view->new_zone_file);
+ view->new_zone_file = NULL;
+ }
+
+#ifdef HAVE_LMDB
+ if (view->new_zone_db != NULL) {
+ isc_mem_free(view->mctx, view->new_zone_db);
+ view->new_zone_db = NULL;
+ }
+ if (env != NULL) {
+ mdb_env_close(env);
+ }
+#endif /* HAVE_LMDB */
+ view->new_zone_config = NULL;
+ view->cfg_destroy = NULL;
+ }
+
+ return (result);
+}
+
+isc_result_t
+dns_view_searchdlz(dns_view_t *view, dns_name_t *name, unsigned int minlabels,
+ dns_clientinfomethods_t *methods,
+ dns_clientinfo_t *clientinfo,
+ 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 *dlzdb;
+ dns_db_t *db, *best = NULL;
+
+ /*
+ * Performs checks to make sure data is as we expect it to be.
+ */
+ REQUIRE(DNS_VIEW_VALID(view));
+ REQUIRE(name != NULL);
+ REQUIRE(dbp != NULL && *dbp == NULL);
+
+ /* setup a "fixed" dns name */
+ zonename = dns_fixedname_initname(&fname);
+
+ /* count the number of labels in the name */
+ namelabels = dns_name_countlabels(name);
+
+ for (dlzdb = ISC_LIST_HEAD(view->dlz_searched);
+ dlzdb != NULL;
+ dlzdb = ISC_LIST_NEXT(dlzdb, link))
+ {
+ REQUIRE(DNS_DLZ_VALID(dlzdb));
+
+ /*
+ * 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 neither the standard
+ * database nor any previous DLZ database had a zone name
+ * match. Otherwise minlabels is the number of labels
+ * in that name. We need to beat that for a "better"
+ * match for this DLZ database to be authoritative.
+ */
+ 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 */
+ db = NULL;
+ findzone = dlzdb->implementation->methods->findzone;
+ result = (*findzone)(dlzdb->implementation->driverarg,
+ dlzdb->dbdata, dlzdb->mctx,
+ view->rdclass, zonename,
+ methods, clientinfo, &db);
+
+ if (result != ISC_R_NOTFOUND) {
+ if (best != NULL)
+ dns_db_detach(&best);
+ if (result == ISC_R_SUCCESS) {
+ INSIST(db != NULL);
+ dns_db_attach(db, &best);
+ dns_db_detach(&db);
+ minlabels = i;
+ } else {
+ if (db != NULL)
+ dns_db_detach(&db);
+ break;
+ }
+ } else if (db != NULL)
+ dns_db_detach(&db);
+ }
+ }
+
+ if (best != NULL) {
+ dns_db_attach(best, dbp);
+ dns_db_detach(&best);
+ return (ISC_R_SUCCESS);
+ }
+
+ return (ISC_R_NOTFOUND);
+}
+
+uint32_t
+dns_view_getfailttl(dns_view_t *view) {
+ REQUIRE(DNS_VIEW_VALID(view));
+ return (view->fail_ttl);
+}
+
+void
+dns_view_setfailttl(dns_view_t *view, uint32_t fail_ttl) {
+ REQUIRE(DNS_VIEW_VALID(view));
+ view->fail_ttl = fail_ttl;
+}
+
+isc_result_t
+dns_view_saventa(dns_view_t *view) {
+ isc_result_t result;
+ bool removefile = false;
+ dns_ntatable_t *ntatable = NULL;
+ FILE *fp = NULL;
+
+ REQUIRE(DNS_VIEW_VALID(view));
+
+ if (view->nta_lifetime == 0)
+ return (ISC_R_SUCCESS);
+
+ /* Open NTA save file for overwrite. */
+ CHECK(isc_stdio_open(view->nta_file, "w", &fp));
+
+ result = dns_view_getntatable(view, &ntatable);
+ if (result == ISC_R_NOTFOUND) {
+ removefile = true;
+ result = ISC_R_SUCCESS;
+ goto cleanup;
+ } else
+ CHECK(result);
+
+ result = dns_ntatable_save(ntatable, fp);
+ if (result == ISC_R_NOTFOUND) {
+ removefile = true;
+ result = ISC_R_SUCCESS;
+ } else if (result == ISC_R_SUCCESS) {
+ result = isc_stdio_close(fp);
+ fp = NULL;
+ }
+
+ cleanup:
+ if (ntatable != NULL)
+ dns_ntatable_detach(&ntatable);
+
+ if (fp != NULL)
+ (void)isc_stdio_close(fp);
+
+ /* Don't leave half-baked NTA save files lying around. */
+ if (result != ISC_R_SUCCESS || removefile)
+ (void) isc_file_remove(view->nta_file);
+
+ return (result);
+}
+
+#define TSTR(t) ((t).value.as_textregion.base)
+#define TLEN(t) ((t).value.as_textregion.length)
+
+isc_result_t
+dns_view_loadnta(dns_view_t *view) {
+ isc_result_t result;
+ dns_ntatable_t *ntatable = NULL;
+ isc_lex_t *lex = NULL;
+ isc_token_t token;
+ isc_stdtime_t now;
+
+ REQUIRE(DNS_VIEW_VALID(view));
+
+ if (view->nta_lifetime == 0)
+ return (ISC_R_SUCCESS);
+
+ CHECK(isc_lex_create(view->mctx, 1025, &lex));
+ CHECK(isc_lex_openfile(lex, view->nta_file));
+ CHECK(dns_view_getntatable(view, &ntatable));
+ isc_stdtime_get(&now);
+
+ for (;;) {
+ int options = (ISC_LEXOPT_EOL | ISC_LEXOPT_EOF);
+ char *name, *type, *timestamp;
+ size_t len;
+ dns_fixedname_t fn;
+ dns_name_t *ntaname;
+ isc_buffer_t b;
+ isc_stdtime_t t;
+ bool forced;
+
+ CHECK(isc_lex_gettoken(lex, options, &token));
+ if (token.type == isc_tokentype_eof)
+ break;
+ else if (token.type != isc_tokentype_string)
+ CHECK(ISC_R_UNEXPECTEDTOKEN);
+ name = TSTR(token);
+ len = TLEN(token);
+
+ if (strcmp(name, ".") == 0)
+ ntaname = dns_rootname;
+ else {
+ ntaname = dns_fixedname_initname(&fn);
+
+ isc_buffer_init(&b, name, (unsigned int)len);
+ isc_buffer_add(&b, (unsigned int)len);
+ CHECK(dns_name_fromtext(ntaname, &b, dns_rootname,
+ 0, NULL));
+ }
+
+ CHECK(isc_lex_gettoken(lex, options, &token));
+ if (token.type != isc_tokentype_string)
+ CHECK(ISC_R_UNEXPECTEDTOKEN);
+ type = TSTR(token);
+
+ if (strcmp(type, "regular") == 0)
+ forced = false;
+ else if (strcmp(type, "forced") == 0)
+ forced = true;
+ else
+ CHECK(ISC_R_UNEXPECTEDTOKEN);
+
+ CHECK(isc_lex_gettoken(lex, options, &token));
+ if (token.type != isc_tokentype_string)
+ CHECK(ISC_R_UNEXPECTEDTOKEN);
+ timestamp = TSTR(token);
+ CHECK(dns_time32_fromtext(timestamp, &t));
+
+ CHECK(isc_lex_gettoken(lex, options, &token));
+ if (token.type != isc_tokentype_eol &&
+ token.type != isc_tokentype_eof)
+ CHECK(ISC_R_UNEXPECTEDTOKEN);
+
+ if (now <= t) {
+ if (t > (now + 604800))
+ t = now + 604800;
+
+ (void) dns_ntatable_add(ntatable, ntaname,
+ forced, 0, t);
+ } else {
+ char nb[DNS_NAME_FORMATSIZE];
+ dns_name_format(ntaname, nb, sizeof(nb));
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC,
+ DNS_LOGMODULE_NTA, ISC_LOG_INFO,
+ "ignoring expired NTA at %s", nb);
+ }
+ };
+
+ cleanup:
+ if (ntatable != NULL)
+ dns_ntatable_detach(&ntatable);
+
+ if (lex != NULL) {
+ isc_lex_close(lex);
+ isc_lex_destroy(&lex);
+ }
+
+ return (result);
+}
+
+void
+dns_view_setviewcommit(dns_view_t *view) {
+ REQUIRE(DNS_VIEW_VALID(view));
+
+ LOCK(&view->lock);
+
+ if (view->redirect != NULL) {
+ dns_zone_setviewcommit(view->redirect);
+ }
+ if (view->managed_keys != NULL) {
+ dns_zone_setviewcommit(view->managed_keys);
+ }
+ if (view->zonetable != NULL) {
+ dns_zt_setviewcommit(view->zonetable);
+ }
+
+ UNLOCK(&view->lock);
+}
+
+void
+dns_view_setviewrevert(dns_view_t *view) {
+ dns_zt_t *zonetable;
+
+ REQUIRE(DNS_VIEW_VALID(view));
+
+ /*
+ * dns_zt_setviewrevert() attempts to lock this view, so we must
+ * release the lock.
+ */
+ LOCK(&view->lock);
+ if (view->redirect != NULL) {
+ dns_zone_setviewrevert(view->redirect);
+ }
+ if (view->managed_keys != NULL) {
+ dns_zone_setviewrevert(view->managed_keys);
+ }
+ zonetable = view->zonetable;
+ UNLOCK(&view->lock);
+
+ if (zonetable != NULL) {
+ dns_zt_setviewrevert(zonetable);
+ }
+}
diff --git a/lib/dns/win32/DLLMain.c b/lib/dns/win32/DLLMain.c
new file mode 100644
index 0000000..8ff1017
--- /dev/null
+++ b/lib/dns/win32/DLLMain.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#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.in b/lib/dns/win32/gen.dsp.in
new file mode 100644
index 0000000..fe172aa
--- /dev/null
+++ b/lib/dns/win32/gen.dsp.in
@@ -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 "@BUILD_PLATFORM@ (x86) Console Application" 0x0103
+
+CFG=gen - @BUILD_PLATFORM@ 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 - @BUILD_PLATFORM@ Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "gen - @BUILD_PLATFORM@ Release" (based on "@BUILD_PLATFORM@ (x86) Console Application")
+!MESSAGE "gen - @BUILD_PLATFORM@ Debug" (based on "@BUILD_PLATFORM@ (x86) Console Application")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "gen - @BUILD_PLATFORM@ 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 @COPTX@ @COPTI@ /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" @COPTY@ /FD /c
+# ADD CPP /nologo /W3 @COPTX@ @COPTI@ /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" @COPTY@ /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 @BUILD_MACHINE@
+# ADD LINK32 user32.lib advapi32.lib /nologo /subsystem:console @BUILD_MACHINE@ /out:"../gen.exe"
+
+!ELSEIF "$(CFG)" == "gen - @BUILD_PLATFORM@ 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 @COPTX@ @COPTI@ /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" @COPTY@ /FD /GZ /c
+# ADD CPP /nologo /W3 /Gm @COPTX@ @COPTI@ /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 @COPTY@
+# 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 @BUILD_MACHINE@ /pdbtype:sept
+# ADD LINK32 user32.lib advapi32.lib /nologo /subsystem:console /debug @BUILD_MACHINE@ /out:"../gen.exe" /pdbtype:sept
+
+!ENDIF
+
+# Begin Target
+
+# Name "gen - @BUILD_PLATFORM@ Release"
+# Name "gen - @BUILD_PLATFORM@ 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.in b/lib/dns/win32/gen.mak.in
new file mode 100644
index 0000000..57533ef
--- /dev/null
+++ b/lib/dns/win32/gen.mak.in
@@ -0,0 +1,267 @@
+# Microsoft Developer Studio Generated NMAKE File, Based on gen.dsp
+!IF "$(CFG)" == ""
+CFG=gen - @BUILD_PLATFORM@ Debug
+!MESSAGE No configuration specified. Defaulting to gen - @BUILD_PLATFORM@ Debug.
+!ENDIF
+
+!IF "$(CFG)" != "gen - @BUILD_PLATFORM@ Release" && "$(CFG)" != "gen - @BUILD_PLATFORM@ 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 - @BUILD_PLATFORM@ Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "gen - @BUILD_PLATFORM@ Release" (based on "@BUILD_PLATFORM@ (x86) Console Application")
+!MESSAGE "gen - @BUILD_PLATFORM@ Debug" (based on "@BUILD_PLATFORM@ (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 - @BUILD_PLATFORM@ 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 - @BUILD_PLATFORM@ 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 @COPTML@ /W3 @COPTX@ @COPTI@ /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" @COPTY@ /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" @BUILD_MACHINE@ /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 - @BUILD_PLATFORM@ 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 @COPTMLD@ /W3 /Gm @COPTX@ @COPTI@ /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 @BUILD_MACHINE@ /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 - @BUILD_PLATFORM@ Release" || "$(CFG)" == "gen - @BUILD_PLATFORM@ Debug"
+SOURCE=..\gen.c
+
+!IF "$(CFG)" == "gen - @BUILD_PLATFORM@ Release"
+
+
+"$(INTDIR)\gen.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "gen - @BUILD_PLATFORM@ 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/gen.vcxproj.filters.in b/lib/dns/win32/gen.vcxproj.filters.in
new file mode 100644
index 0000000..f6b3bb0
--- /dev/null
+++ b/lib/dns/win32/gen.vcxproj.filters.in
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\gen.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\gen-win32.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/lib/dns/win32/gen.vcxproj.in b/lib/dns/win32/gen.vcxproj.in
new file mode 100644
index 0000000..2d6d8e0
--- /dev/null
+++ b/lib/dns/win32/gen.vcxproj.in
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|@BUILD_PLATFORM@">
+ <Configuration>Debug</Configuration>
+ <Platform>@BUILD_PLATFORM@</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|@BUILD_PLATFORM@">
+ <Configuration>Release</Configuration>
+ <Platform>@BUILD_PLATFORM@</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{A3F71D12-F38A-4C77-8D87-8E8854CA74A1}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>gen</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@BUILD_PLATFORM@'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@BUILD_PLATFORM@'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|@BUILD_PLATFORM@'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|@BUILD_PLATFORM@'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@BUILD_PLATFORM@'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>..\</OutDir>
+ <IntDir>.\$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@BUILD_PLATFORM@'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>..\</OutDir>
+ <IntDir>.\$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@BUILD_PLATFORM@'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>.\;..\..\..\;..\..\isc\win32;..\..\isc\win32\include;..\..\isc\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <PrecompiledHeaderOutputFile>.\$(Configuration)\$(TargetName).pch</PrecompiledHeaderOutputFile>
+ <ProgramDataBaseFileName>$(OutDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <BrowseInformation>true</BrowseInformation>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <AssemblerListingLocation>.\$(Configuration)\</AssemblerListingLocation>
+ <ObjectFileName>.\$(Configuration)\</ObjectFileName>
+ <CompileAs>CompileAsC</CompileAs>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <OutputFile>..\$(TargetName)$(TargetExt)</OutputFile>
+ </Link>
+ <PostBuildEvent>
+ <Command>cd ..
+gen -s . -t &gt; include\dns\enumtype.h
+gen -s . -c &gt; include\dns\enumclass.h
+gen -s . -i -P ./rdata/rdatastructpre.h -S ./rdata/rdatastructsuf.h &gt; include\dns\rdatastruct.h
+gen -s . &gt; code.h
+</Command>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|@BUILD_PLATFORM@'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>@INTRINSIC@</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>.\;..\..\..\;..\..\isc\win32;..\..\isc\win32\include;..\..\isc\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <StringPooling>true</StringPooling>
+ <PrecompiledHeaderOutputFile>.\$(Configuration)\$(TargetName).pch</PrecompiledHeaderOutputFile>
+ <ProgramDataBaseFileName>$(OutDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ <AssemblerListingLocation>.\$(Configuration)\</AssemblerListingLocation>
+ <ObjectFileName>.\$(Configuration)\</ObjectFileName>
+ <CompileAs>CompileAsC</CompileAs>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <OutputFile>..\$(TargetName)$(TargetExt)</OutputFile>
+ <LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
+ </Link>
+ <PostBuildEvent>
+ <Command>cd ..
+gen -s . -t &gt; include\dns\enumtype.h
+gen -s . -c &gt; include\dns\enumclass.h
+gen -s . -i -P ./rdata/rdatastructpre.h -S ./rdata/rdatastructsuf.h &gt; include\dns\rdatastruct.h
+gen -s . &gt; code.h
+</Command>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="..\gen.c" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\gen-win32.h" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/lib/dns/win32/gen.vcxproj.user b/lib/dns/win32/gen.vcxproj.user
new file mode 100644
index 0000000..695b5c7
--- /dev/null
+++ b/lib/dns/win32/gen.vcxproj.user
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+</Project> \ No newline at end of file
diff --git a/lib/dns/win32/libdns.def.in b/lib/dns/win32/libdns.def.in
new file mode 100644
index 0000000..62a156c
--- /dev/null
+++ b/lib/dns/win32/libdns.def.in
@@ -0,0 +1,1504 @@
+LIBRARY libdns
+
+; Exported Functions
+EXPORTS
+
+dns__rbt_getheight
+dns__rbt_checkproperties
+dns__rbtnode_getdistance
+dns__zone_findkeys
+dns__zone_loadpending
+dns__zone_updatesigs
+dns_acache_attach
+dns_acache_attachentry
+dns_acache_cancelentry
+dns_acache_countquerymiss
+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_match2
+dns_acl_merge
+dns_acl_none
+dns_aclelement_match
+dns_aclelement_match2
+dns_aclenv_copy
+dns_aclenv_destroy
+dns_aclenv_init
+dns_adb_agesrtt
+dns_adb_adjustsrtt
+dns_adb_attach
+dns_adb_beginudpfetch
+dns_adb_cancelfind
+dns_adb_changeflags
+dns_adb_create
+dns_adb_createfind
+dns_adb_createfind2
+dns_adb_destroyfind
+dns_adb_detach
+dns_adb_dump
+dns_adb_dumpfind
+dns_adb_ednsto
+dns_adb_endudpfetch
+dns_adb_findaddrinfo
+dns_adb_flush
+dns_adb_flushname
+dns_adb_flushnames
+dns_adb_freeaddrinfo
+dns_adb_getcookie
+dns_adb_getudpsize
+dns_adb_marklame
+dns_adb_noedns
+dns_adb_plainresponse
+dns_adb_probesize
+dns_adb_probesize2
+dns_adb_setadbsize
+dns_adb_setcookie
+dns_adb_setquota
+dns_adb_setudpsize
+dns_adb_shutdown
+dns_adb_timeout
+dns_adb_whenshutdown
+dns_adbentry_overquota
+dns_badcache_add
+dns_badcache_destroy
+dns_badcache_find
+dns_badcache_flush
+dns_badcache_flushname
+dns_badcache_flushtree
+dns_badcache_init
+dns_badcache_print
+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_create2
+dns_cache_create3
+dns_cache_detach
+dns_cache_dump
+dns_cache_dumpstats
+dns_cache_flush
+dns_cache_flushname
+dns_cache_flushnode
+dns_cache_getcachesize
+dns_cache_getcleaninginterval
+dns_cache_getname
+dns_cache_getstats
+dns_cache_load
+@IF NOTYET
+dns_cache_renderjson
+@END NOTYET
+@IF LIBXML2
+dns_cache_renderxml
+@END LIBXML2
+dns_cache_setcachesize
+dns_cache_setcleaninginterval
+dns_cache_setfilename
+dns_cache_updatestats
+dns_catz_add_zone
+dns_catz_catzs_attach
+dns_catz_catzs_detach
+dns_catz_catzs_set_view
+dns_catz_dbupdate_callback
+dns_catz_entry_attach
+dns_catz_entry_cmp
+dns_catz_entry_copy
+dns_catz_entry_detach
+dns_catz_entry_getname
+dns_catz_entry_new
+dns_catz_entry_validate
+dns_catz_generate_masterfilename
+dns_catz_generate_zonecfg
+dns_catz_get_iterator
+dns_catz_get_zone
+dns_catz_new_zone
+dns_catz_new_zones
+dns_catz_options_copy
+dns_catz_options_free
+dns_catz_options_init
+dns_catz_options_setdefault
+dns_catz_postreconfig
+dns_catz_prereconfig
+dns_catz_update_from_db
+dns_catz_update_process
+dns_catz_update_taskaction
+dns_catz_zone_attach
+dns_catz_zone_detach
+dns_catz_zone_getdefoptions
+dns_catz_zone_getname
+dns_catz_zone_resetdefoptions
+dns_catz_zones_merge
+dns_cert_fromtext
+dns_cert_totext
+dns_client_addtrustedkey
+dns_client_cancelrequest
+dns_client_cancelresolve
+dns_client_cancelupdate
+dns_client_clearservers
+dns_client_create
+dns_client_createx
+dns_client_createx2
+dns_client_destroy
+dns_client_destroyreqtrans
+dns_client_destroyrestrans
+dns_client_destroyupdatetrans
+dns_client_freeresanswer
+dns_client_freeupdate
+dns_client_mctx
+dns_client_request
+dns_client_resolve
+dns_client_setdlv
+dns_client_setservers
+dns_client_startrequest
+dns_client_startresolve
+dns_client_startupdate
+dns_client_update
+dns_client_updaterec
+dns_clientinfo_init
+dns_clientinfomethods_init
+dns_compress_add
+dns_compress_disable
+dns_compress_findglobal
+dns_compress_getedns
+dns_compress_getmethods
+dns_compress_getsensitive
+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_diffx
+dns_db_dump
+dns_db_dump2
+dns_db_endload
+dns_db_expirenode
+dns_db_getsize
+dns_db_find
+dns_db_findext
+dns_db_findnode
+dns_db_findnodeext
+dns_db_findnsec3node
+dns_db_findrdataset
+dns_db_findzonecut
+dns_db_getnsec3parameters
+dns_db_getoriginnode
+dns_db_getrrsetstats
+dns_db_getsigningtime
+dns_db_getsoaserial
+dns_db_hashsize
+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_load3
+dns_db_newversion
+dns_db_nodecount
+dns_db_nodefullname
+dns_db_ondestroy
+dns_db_origin
+dns_db_overmem
+dns_db_printnode
+dns_db_register
+dns_db_resigned
+dns_db_rpz_attach
+dns_db_rpz_ready
+dns_db_serialize
+dns_db_setcachestats
+dns_db_setsigningtime
+dns_db_settask
+dns_db_subtractrdataset
+dns_db_transfernode
+dns_db_unregister
+dns_db_updatenotify_register
+dns_db_updatenotify_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_addresponse2
+dns_dispatch_addresponse3
+dns_dispatch_attach
+dns_dispatch_cancel
+dns_dispatch_changeattributes
+dns_dispatch_createtcp
+dns_dispatch_createtcp2
+dns_dispatch_detach
+dns_dispatch_getattributes
+dns_dispatch_getdscp
+dns_dispatch_getentrysocket
+dns_dispatch_getlocaladdress
+dns_dispatch_getnext
+dns_dispatch_getsocket
+dns_dispatch_gettcp
+dns_dispatch_gettcp2
+dns_dispatch_getudp
+dns_dispatch_getudp_dup
+dns_dispatch_importrecv
+dns_dispatch_removeresponse
+dns_dispatch_setdscp
+dns_dispatch_starttcp
+dns_dispatchmgr_create
+dns_dispatchmgr_destroy
+dns_dispatchmgr_getblackhole
+dns_dispatchmgr_getblackportlist
+dns_dispatchmgr_setavailports
+dns_dispatchmgr_setblackhole
+dns_dispatchmgr_setblackportlist
+dns_dispatchmgr_setstats
+dns_dispatchset_cancelall
+dns_dispatchset_create
+dns_dispatchset_destroy
+dns_dispatchset_get
+dns_dlz_ssumatch
+dns_dlz_writeablezone
+dns_dlzallowzonexfr
+dns_dlzconfigure
+dns_dlzcreate
+dns_dlzdestroy
+dns_dlzregister
+dns_dlzstrtoargv
+dns_dlzunregister
+dns_dns64_aaaafroma
+dns_dns64_aaaaok
+dns_dns64_append
+dns_dns64_create
+dns_dns64_destroy
+dns_dns64_next
+dns_dns64_unlink
+dns_dnssec_findmatchingkeys
+dns_dnssec_findmatchingkeys2
+dns_dnssec_findzonekeys
+dns_dnssec_findzonekeys2
+dns_dnssec_findzonekeys3
+dns_dnssec_keyactive
+dns_dnssec_keyfromrdata
+dns_dnssec_keylistfromrdataset
+dns_dnssec_selfsigns
+dns_dnssec_sign
+dns_dnssec_signmessage
+dns_dnssec_signs
+dns_dnssec_syncupdate
+dns_dnssec_syncupdate
+dns_dnssec_updatekeys
+dns_dnssec_verify
+dns_dnssec_verify2
+dns_dnssec_verify3
+dns_dnssec_verifymessage
+dns_dnsseckey_create
+dns_dnsseckey_destroy
+dns_ds_buildrdata
+dns_dsdigest_fromtext
+dns_dsdigest_format
+dns_dsdigest_totext
+@IF NOTYET
+dns_dt_attach
+dns_dt_close
+dns_dt_create
+dns_dt_datatotext
+dns_dt_detach
+dns_dt_getframe
+dns_dt_getstats
+dns_dt_open
+dns_dt_parse
+dns_dt_reopen
+dns_dt_send
+dns_dt_setidentity
+dns_dt_setversion
+dns_dt_shutdown
+dns_dtdata_free
+@END NOTYET
+dns_dumpctx_attach
+dns_dumpctx_cancel
+dns_dumpctx_db
+dns_dumpctx_detach
+dns_dumpctx_version
+dns_dyndb_load
+dns_dyndb_cleanup
+dns_dyndb_createctx
+dns_dyndb_destroyctx
+dns_ecdb_register
+dns_ecdb_unregister
+dns_fixedname_init
+dns_fixedname_invalidate
+dns_fixedname_name
+dns_fixedname_initname
+dns_fwdtable_add
+dns_fwdtable_addfwd
+dns_fwdtable_create
+dns_fwdtable_delete
+dns_fwdtable_destroy
+dns_fwdtable_find
+dns_fwdtable_find2
+dns_generalstats_create
+dns_generalstats_dump
+dns_generalstats_increment
+@IF GEOIP
+dns_geoip_match
+dns_geoip_shutdown
+@END GEOIP
+dns_hashalg_fromtext
+dns_ipkeylist_clear
+dns_ipkeylist_copy
+dns_ipkeylist_init
+dns_ipkeylist_resize
+dns_iptable_addprefix
+dns_iptable_addprefix2
+dns_iptable_attach
+dns_iptable_create
+dns_iptable_detach
+dns_iptable_merge
+dns_journal_begin_transaction
+dns_journal_commit
+dns_journal_compact
+dns_journal_current_rr
+dns_journal_destroy
+dns_journal_first_rr
+dns_journal_first_serial
+dns_journal_get_sourceserial
+dns_journal_iter_init
+dns_journal_last_serial
+dns_journal_next_rr
+dns_journal_open
+dns_journal_print
+dns_journal_rollforward
+dns_journal_set_sourceserial
+dns_journal_write_transaction
+dns_journal_writediff
+dns_keydata_fromdnskey
+dns_keydata_todnskey
+dns_keyflags_fromtext
+dns_keynode_attach
+dns_keynode_create
+dns_keynode_detach
+dns_keynode_detachall
+dns_keynode_key
+dns_keynode_managed
+dns_keyring_restore
+dns_keytable_add
+dns_keytable_attach
+dns_keytable_attachkeynode
+dns_keytable_create
+dns_keytable_delete
+dns_keytable_deletekeynode
+dns_keytable_detach
+dns_keytable_detachkeynode
+dns_keytable_dump
+dns_keytable_find
+dns_keytable_finddeepestmatch
+dns_keytable_findkeynode
+dns_keytable_findnextkeynode
+dns_keytable_forall
+dns_keytable_issecuredomain
+dns_keytable_marksecure
+dns_keytable_nextkeynode
+dns_keytable_totext
+dns_lib_init
+dns_lib_initmsgcat
+dns_lib_shutdown
+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_dump2
+dns_master_dump3
+dns_master_dumpinc
+dns_master_dumpinc2
+dns_master_dumpinc3
+dns_master_dumpnode
+dns_master_dumpnodetostream
+dns_master_dumptostream
+dns_master_dumptostream2
+dns_master_dumptostream3
+dns_master_dumptostreaminc
+dns_master_initrawheader
+dns_master_loadbuffer
+dns_master_loadbufferinc
+dns_master_loadfile
+dns_master_loadfile2
+dns_master_loadfile3
+dns_master_loadfile4
+dns_master_loadfile5
+dns_master_loadfileinc
+dns_master_loadfileinc2
+dns_master_loadfileinc3
+dns_master_loadfileinc4
+dns_master_loadfileinc5
+dns_master_loadlexer
+dns_master_loadlexerinc
+dns_master_loadstream
+dns_master_loadstreaminc
+dns_master_questiontotext
+dns_master_rdatasettotext
+dns_master_stylecreate
+dns_master_stylecreate2
+dns_master_styledestroy
+dns_master_styleflags
+dns_message_addname
+dns_message_buildopt
+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_logfmtpacket
+dns_message_logfmtpacket2
+dns_message_logpacket
+dns_message_logpacket2
+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_setclass
+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_caseequal
+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_fromstring
+dns_name_fromstring2
+dns_name_fromtext
+dns_name_fromwire
+dns_name_fullcompare
+dns_name_fullhash
+dns_name_getlabel
+dns_name_getlabelsequence
+dns_name_hasbuffer
+dns_name_hash
+dns_name_hashbylabel
+dns_name_init
+dns_name_internalwildcard
+dns_name_invalidate
+dns_name_isabsolute
+dns_name_isdnssd
+dns_name_ishostname
+dns_name_ismailbox
+dns_name_isrfc1918
+dns_name_issubdomain
+dns_name_istat
+dns_name_isula
+dns_name_isvalid
+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_toprincipal
+dns_name_toregion
+dns_name_tostring
+dns_name_totext
+dns_name_totext2
+dns_name_towire
+dns_ncache_add
+dns_ncache_addoptout
+dns_ncache_current
+dns_ncache_getrdataset
+dns_ncache_getsigrdataset
+dns_ncache_towire
+dns_nsec3_active
+dns_nsec3_activex
+dns_nsec3_addnsec3
+dns_nsec3_addnsec3s
+dns_nsec3_addnsec3sx
+dns_nsec3_buildrdata
+dns_nsec3_delnsec3
+dns_nsec3_delnsec3s
+dns_nsec3_delnsec3sx
+dns_nsec3_hashlength
+dns_nsec3_hashname
+dns_nsec3_maxiterations
+dns_nsec3_noexistnodata
+dns_nsec3_supportedhash
+dns_nsec3_typepresent
+dns_nsec3param_deletechains
+dns_nsec3param_fromprivate
+dns_nsec3param_salttotext
+dns_nsec3param_toprivate
+dns_nsec_build
+dns_nsec_buildrdata
+dns_nsec_compressbitmap
+dns_nsec_isset
+dns_nsec_noexistnodata
+dns_nsec_nseconly
+dns_nsec_setbit
+dns_nsec_typepresent
+dns_ntatable_add
+dns_ntatable_attach
+dns_ntatable_covered
+dns_ntatable_create
+dns_ntatable_delete
+dns_ntatable_detach
+dns_ntatable_dump
+dns_ntatable_save
+dns_ntatable_totext
+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_getednsversion
+dns_peer_getforcetcp
+dns_peer_getkey
+dns_peer_getmaxudp
+dns_peer_getnotifydscp
+dns_peer_getnotifysource
+dns_peer_getprovideixfr
+dns_peer_getquerydscp
+dns_peer_getquerysource
+dns_peer_getrequestexpire
+dns_peer_getrequestixfr
+dns_peer_getrequestnsid
+dns_peer_getsendcookie
+dns_peer_getsupportedns
+dns_peer_gettransferdscp
+dns_peer_gettransferformat
+dns_peer_gettransfers
+dns_peer_gettransfersource
+dns_peer_getudpsize
+dns_peer_new
+dns_peer_newprefix
+dns_peer_setbogus
+dns_peer_setednsversion
+dns_peer_setforcetcp
+dns_peer_setkey
+dns_peer_setkeybycharp
+dns_peer_setmaxudp
+dns_peer_setnotifydscp
+dns_peer_setnotifysource
+dns_peer_setprovideixfr
+dns_peer_setquerydscp
+dns_peer_setquerysource
+dns_peer_setrequestexpire
+dns_peer_setrequestixfr
+dns_peer_setrequestnsid
+dns_peer_setsendcookie
+dns_peer_setsupportedns
+dns_peer_settransferdscp
+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_attach
+dns_portlist_create
+dns_portlist_detach
+dns_portlist_match
+dns_portlist_remove
+dns_private_chains
+dns_private_totext
+dns_rbt_addname
+dns_rbt_addnode
+dns_rbt_create
+dns_rbt_deletename
+dns_rbt_deletenode
+dns_rbt_deserialize_tree
+dns_rbt_destroy
+dns_rbt_destroy2
+dns_rbt_findname
+dns_rbt_findnode
+dns_rbt_formatnodename
+dns_rbt_fullnamefromnode
+dns_rbt_hashsize
+dns_rbt_namefromnode
+dns_rbt_nodecount
+dns_rbt_printdot
+dns_rbt_printnodeinfo
+dns_rbt_printtext
+dns_rbt_root
+dns_rbt_serialize_align
+dns_rbt_serialize_tree
+dns_rbtnode_nodename
+dns_rbtnodechain_current
+dns_rbtnodechain_down
+dns_rbtnodechain_first
+dns_rbtnodechain_init
+dns_rbtnodechain_invalidate
+dns_rbtnodechain_last
+dns_rbtnodechain_next
+dns_rbtnodechain_nextflat
+dns_rbtnodechain_prev
+dns_rbtnodechain_reset
+dns_rcode_fromtext
+dns_rcode_totext
+dns_rcodestats_create
+dns_rcodestats_dump
+dns_rcodestats_increment
+dns_rdata_additionaldata
+dns_rdata_apl_current
+dns_rdata_apl_first
+dns_rdata_apl_next
+dns_rdata_casecompare
+dns_rdata_checknames
+dns_rdata_checkowner
+dns_rdata_clone
+dns_rdata_compare
+dns_rdata_covers
+dns_rdata_deleterrset
+dns_rdata_digest
+dns_rdata_exists
+dns_rdata_freestruct
+dns_rdata_fromregion
+dns_rdata_fromstruct
+dns_rdata_fromtext
+dns_rdata_fromwire
+dns_rdata_hip_current
+dns_rdata_hip_first
+dns_rdata_hip_next
+dns_rdata_init
+dns_rdata_makedelete
+dns_rdata_notexist
+dns_rdata_opt_current
+dns_rdata_opt_first
+dns_rdata_opt_next
+dns_rdata_reset
+dns_rdata_tofmttext
+dns_rdata_toregion
+dns_rdata_tostruct
+dns_rdata_totext
+dns_rdata_towire
+dns_rdata_txt_current
+dns_rdata_txt_first
+dns_rdata_txt_next
+dns_rdata_updateop
+dns_rdatacallbacks_init
+dns_rdatacallbacks_init_stdio
+dns_rdataclass_format
+dns_rdataclass_fromtext
+dns_rdataclass_ismeta
+dns_rdataclass_totext
+dns_rdataclass_tounknowntext
+dns_rdatalist_fromrdataset
+dns_rdatalist_init
+dns_rdatalist_tordataset
+dns_rdataset_addclosest
+dns_rdataset_additionaldata
+dns_rdataset_addnoqname
+dns_rdataset_clearprefetch
+dns_rdataset_clone
+dns_rdataset_count
+dns_rdataset_current
+dns_rdataset_disassociate
+dns_rdataset_expire
+dns_rdataset_first
+dns_rdataset_getadditional
+dns_rdataset_getclosest
+dns_rdataset_getnoqname
+dns_rdataset_getownercase
+dns_rdataset_init
+dns_rdataset_invalidate
+dns_rdataset_isassociated
+dns_rdataset_makequestion
+dns_rdataset_next
+dns_rdataset_putadditional
+dns_rdataset_setadditional
+dns_rdataset_setownercase
+dns_rdataset_settrust
+dns_rdataset_totext
+dns_rdataset_towire
+dns_rdataset_towirepartial
+dns_rdataset_towiresorted
+dns_rdataset_trimttl
+dns_rdatasetiter_current
+dns_rdatasetiter_destroy
+dns_rdatasetiter_first
+dns_rdatasetiter_next
+dns_rdatasetstats_create
+dns_rdatasetstats_decrement
+dns_rdatasetstats_dump
+dns_rdatasetstats_increment
+dns_rdataslab_count
+dns_rdataslab_equal
+dns_rdataslab_equalx
+dns_rdataslab_fromrdataset
+dns_rdataslab_merge
+dns_rdataslab_size
+dns_rdataslab_subtract
+dns_rdataslab_tordataset
+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_rdatatype_tounknowntext
+dns_rdatatypestats_create
+dns_rdatatypestats_dump
+dns_rdatatypestats_increment
+dns_request_cancel
+dns_request_create
+dns_request_createraw
+dns_request_createraw2
+dns_request_createraw3
+dns_request_createraw4
+dns_request_createvia
+dns_request_createvia2
+dns_request_createvia3
+dns_request_createvia4
+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_addbadcache
+dns_resolver_algorithm_supported
+dns_resolver_attach
+dns_resolver_cancelfetch
+dns_resolver_create
+dns_resolver_createfetch
+dns_resolver_createfetch2
+dns_resolver_createfetch3
+dns_resolver_destroyfetch
+dns_resolver_detach
+dns_resolver_disable_algorithm
+dns_resolver_disable_ds_digest
+dns_resolver_dispatchmgr
+dns_resolver_dispatchv4
+dns_resolver_dispatchv6
+dns_resolver_ds_digest_supported
+dns_resolver_dumpfetches
+dns_resolver_flushbadcache
+dns_resolver_flushbadnames
+dns_resolver_freeze
+dns_resolver_getbadcache
+dns_resolver_getclientsperquery
+dns_resolver_getlamettl
+dns_resolver_getmaxdepth
+dns_resolver_getmaxqueries
+dns_resolver_getmustbesecure
+dns_resolver_getoptions
+dns_resolver_getquerydscp4
+dns_resolver_getquerydscp6
+dns_resolver_getquotaresponse
+dns_resolver_gettimeout
+dns_resolver_getudpsize
+dns_resolver_getzeronosoattl
+dns_resolver_logfetch
+dns_resolver_nrunning
+dns_resolver_prime
+dns_resolver_printbadcache
+dns_resolver_reset_algorithms
+dns_resolver_reset_ds_digests
+dns_resolver_resetmustbesecure
+dns_resolver_setclientsperquery
+dns_resolver_setfetchesperzone
+dns_resolver_setlamettl
+dns_resolver_setmaxdepth
+dns_resolver_setmaxqueries
+dns_resolver_setmustbesecure
+dns_resolver_setquerydscp4
+dns_resolver_setquerydscp6
+dns_resolver_setquotaresponse
+dns_resolver_settimeout
+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_root_checkhints
+dns_rootns_create
+dns_rpz_add
+dns_rpz_attach_rpzs
+dns_rpz_beginload
+dns_rpz_decode_cname
+dns_rpz_delete
+dns_rpz_detach_rpzs
+dns_rpz_find_ip
+dns_rpz_find_name
+dns_rpz_new_zones
+dns_rpz_policy2str
+dns_rpz_ready
+dns_rpz_str2policy
+dns_rpz_type2str
+dns_rriterator_current
+dns_rriterator_destroy
+dns_rriterator_first
+dns_rriterator_init
+dns_rriterator_next
+dns_rriterator_nextrrset
+dns_rriterator_pause
+dns_rrl
+dns_rrl_init
+dns_rrl_view_destroy
+dns_sdb_putnamedrdata
+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_sdlz_setdb
+dns_sdlzregister
+dns_sdlzunregister
+dns_secalg_format
+dns_secalg_fromtext
+dns_secalg_totext
+dns_secproto_fromtext
+dns_secproto_totext
+dns_soa_buildrdata
+dns_soa_getexpire
+dns_soa_getminimum
+dns_soa_getrefresh
+dns_soa_getretry
+dns_soa_getserial
+dns_soa_setexpire
+dns_soa_setminimum
+dns_soa_setrefresh
+dns_soa_setretry
+dns_soa_setserial
+dns_ssu_external_match
+dns_ssu_mtypefromstring
+dns_ssutable_addrule
+dns_ssutable_attach
+dns_ssutable_checkrules
+dns_ssutable_checkrules2
+dns_ssutable_create
+dns_ssutable_createdlz
+dns_ssutable_detach
+dns_stats_alloccounters
+dns_stats_attach
+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_from32
+dns_time64_fromtext
+dns_time64_totext
+dns_timer_setidle
+dns_tkey_builddeletequery
+dns_tkey_builddhquery
+dns_tkey_buildgssquery
+dns_tkey_gssnegotiate
+dns_tkey_processdeleteresponse
+dns_tkey_processdhresponse
+dns_tkey_processgssresponse
+dns_tkey_processquery
+dns_tkeyctx_create
+dns_tkeyctx_destroy
+dns_trust_totext
+dns_tsec_create
+dns_tsec_destroy
+dns_tsec_getkey
+dns_tsec_gettype
+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_add
+dns_tsigkeyring_attach
+dns_tsigkeyring_create
+dns_tsigkeyring_detach
+dns_tsigkeyring_dumpanddetach
+dns_tsigrcode_fromtext
+dns_tsigrcode_totext
+dns_ttl_fromtext
+dns_ttl_totext
+dns_ttl_totext2
+dns_update_signatures
+dns_update_signaturesinc
+dns_update_soaserial
+dns_validator_cancel
+dns_validator_create
+dns_validator_destroy
+dns_validator_send
+dns_view_adddelegationonly
+dns_view_addzone
+dns_view_asyncload
+dns_view_asyncload2
+dns_view_attach
+dns_view_checksig
+dns_view_create
+dns_view_createresolver
+dns_view_createzonetable
+dns_view_detach
+dns_view_dialup
+dns_view_dumpdbtostream
+dns_view_excludedelegationonly
+dns_view_find
+dns_view_find2
+dns_view_findzone
+dns_view_findzonecut
+dns_view_findzonecut2
+dns_view_flushanddetach
+dns_view_flushcache
+dns_view_flushcache2
+dns_view_flushname
+dns_view_flushnode
+dns_view_freeze
+dns_view_freezezones
+dns_view_getadbstats
+dns_view_getdynamickeyring
+dns_view_getfailttl
+dns_view_getntatable
+dns_view_getpeertsig
+dns_view_getresquerystats
+dns_view_getresstats
+dns_view_getrootdelonly
+dns_view_getsecroots
+dns_view_gettsig
+dns_view_initntatable
+dns_view_initsecroots
+dns_view_iscacheshared
+dns_view_isdelegationonly
+dns_view_issecuredomain
+dns_view_load
+dns_view_loadnew
+dns_view_loadnta
+dns_view_ntacovers
+dns_view_restorekeyring
+dns_view_saventa
+dns_view_searchdlz
+dns_view_setadbstats
+dns_view_setcache
+dns_view_setcache2
+dns_view_setdstport
+dns_view_setdynamickeyring
+dns_view_setfailttl
+dns_view_sethints
+dns_view_setkeyring
+dns_view_setnewzones
+dns_view_setresquerystats
+dns_view_setresstats
+dns_view_setrootdelonly
+dns_view_setviewcommit
+dns_view_setviewrevert
+dns_view_simplefind
+dns_view_thaw
+dns_view_untrust
+dns_view_weakattach
+dns_view_weakdetach
+dns_viewlist_find
+dns_viewlist_findzone
+dns_xfrin_attach
+dns_xfrin_create
+dns_xfrin_create2
+dns_xfrin_create3
+dns_xfrin_detach
+dns_xfrin_shutdown
+dns_zone_addnsec3chain
+dns_zone_asyncload
+dns_zone_asyncload2
+dns_zone_attach
+dns_zone_catz_enable
+dns_zone_catz_enable_db
+dns_zone_cdscheck
+dns_zone_checknames
+dns_zone_clearforwardacl
+dns_zone_clearnotifyacl
+dns_zone_clearqueryacl
+dns_zone_clearqueryonacl
+dns_zone_clearupdateacl
+dns_zone_clearxfracl
+dns_zone_create
+dns_zone_detach
+dns_zone_dialup
+dns_zone_dlzpostload
+dns_zone_dump
+dns_zone_dumptostream
+dns_zone_dumptostream2
+dns_zone_dumptostream3
+dns_zone_expire
+dns_zone_first
+dns_zone_flush
+dns_zone_forcereload
+dns_zone_forwardupdate
+dns_zone_fulldumptostream
+dns_zone_get_parentcatz
+dns_zone_get_rpz_num
+dns_zone_getadded
+dns_zone_getaltxfrsource4
+dns_zone_getaltxfrsource4dscp
+dns_zone_getaltxfrsource6
+dns_zone_getaltxfrsource6dscp
+dns_zone_getautomatic
+dns_zone_getchecknames
+dns_zone_getclass
+dns_zone_getdb
+dns_zone_getdbtype
+dns_zone_getexpiretime
+dns_zone_getfile
+dns_zone_getforwardacl
+dns_zone_getidlein
+dns_zone_getidleout
+dns_zone_getincludes
+dns_zone_getjournal
+dns_zone_getjournalsize
+dns_zone_getkeydirectory
+dns_zone_getkeyopts
+dns_zone_getloadtime
+dns_zone_getmaxrecords
+dns_zone_getmaxttl
+dns_zone_getmaxxfrin
+dns_zone_getmaxxfrout
+dns_zone_getmctx
+dns_zone_getmgr
+dns_zone_getnotifyacl
+dns_zone_getnotifydelay
+dns_zone_getnotifysrc4
+dns_zone_getnotifysrc4dscp
+dns_zone_getnotifysrc6
+dns_zone_getnotifysrc6dscp
+dns_zone_getoptions
+dns_zone_getoptions2
+dns_zone_getorigin
+dns_zone_getprivatetype
+dns_zone_getqueryacl
+dns_zone_getqueryonacl
+dns_zone_getraw
+dns_zone_getrcvquerystats
+dns_zone_getrefreshkeytime
+dns_zone_getrefreshtime
+dns_zone_getrequestexpire
+dns_zone_getrequestixfr
+dns_zone_getrequeststats
+dns_zone_getserial
+dns_zone_getserial2
+dns_zone_getserialupdatemethod
+dns_zone_getsignatures
+dns_zone_getsigresigninginterval
+dns_zone_getsigvalidityinterval
+dns_zone_getssutable
+dns_zone_getstatlevel
+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_getxfrsource4dscp
+dns_zone_getxfrsource6
+dns_zone_getxfrsource6dscp
+dns_zone_getzeronosoattl
+dns_zone_iattach
+dns_zone_idetach
+dns_zone_isdynamic
+dns_zone_isforced
+dns_zone_keydone
+dns_zone_link
+dns_zone_load
+dns_zone_loadandthaw
+dns_zone_loadnew
+dns_zone_log
+dns_zone_logc
+dns_zone_logv
+dns_zone_maintenance
+dns_zone_markdirty
+dns_zone_name
+dns_zone_nameonly
+dns_zone_next
+dns_zone_notify
+dns_zone_notifyreceive
+dns_zone_notifyreceive2
+dns_zone_nscheck
+dns_zone_refresh
+dns_zone_rekey
+dns_zone_replacedb
+dns_zone_rpz_enable
+dns_zone_rpz_enable_db
+dns_zone_set_parentcatz
+dns_zone_setacache
+dns_zone_setadded
+dns_zone_setalsonotify
+dns_zone_setalsonotifydscpkeys
+dns_zone_setalsonotifywithkeys
+dns_zone_setaltxfrsource4
+dns_zone_setaltxfrsource4dscp
+dns_zone_setaltxfrsource6
+dns_zone_setaltxfrsource6dscp
+dns_zone_setautomatic
+dns_zone_setcheckmx
+dns_zone_setchecknames
+dns_zone_setcheckns
+dns_zone_setchecksrv
+dns_zone_setclass
+dns_zone_setdb
+dns_zone_setdbtype
+dns_zone_setdialup
+dns_zone_setfile
+dns_zone_setfile2
+dns_zone_setfile3
+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_setkeyopt
+dns_zone_setmasters
+dns_zone_setmasterswithkeys
+dns_zone_setmaxrecords
+dns_zone_setmaxrefreshtime
+dns_zone_setmaxretrytime
+dns_zone_setmaxttl
+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_setnotifysrc4dscp
+dns_zone_setnotifysrc6
+dns_zone_setnotifysrc6dscp
+dns_zone_setnotifytype
+dns_zone_setnsec3param
+dns_zone_setoption
+dns_zone_setoption2
+dns_zone_setorigin
+dns_zone_setprivatetype
+dns_zone_setqueryacl
+dns_zone_setqueryonacl
+dns_zone_setrawdata
+dns_zone_setrcvquerystats
+dns_zone_setrefreshkeyinterval
+dns_zone_setrequestexpire
+dns_zone_setrequestixfr
+dns_zone_setrequeststats
+dns_zone_setserial
+dns_zone_setserialupdatemethod
+dns_zone_setsignatures
+dns_zone_setsigresigninginterval
+dns_zone_setsigvalidityinterval
+dns_zone_setssutable
+dns_zone_setstatistics
+dns_zone_setstatlevel
+dns_zone_setstats
+dns_zone_settask
+dns_zone_settype
+dns_zone_setupdateacl
+dns_zone_setupdatedisabled
+dns_zone_setview
+dns_zone_setviewcommit
+dns_zone_setviewrevert
+dns_zone_setxfracl
+dns_zone_setxfrsource4
+dns_zone_setxfrsource4dscp
+dns_zone_setxfrsource6
+dns_zone_setxfrsource6dscp
+dns_zone_setzeronosoattl
+dns_zone_signwithkey
+dns_zone_synckeyzone
+dns_zone_unload
+dns_zonekey_iszonekey
+dns_zonemgr_attach
+dns_zonemgr_create
+dns_zonemgr_createzone
+dns_zonemgr_detach
+dns_zonemgr_forcemaint
+dns_zonemgr_getcount
+dns_zonemgr_getiolimit
+dns_zonemgr_getnotifyrate
+dns_zonemgr_getserialqueryrate
+dns_zonemgr_getstartupnotifyrate
+dns_zonemgr_getttransfersin
+dns_zonemgr_getttransfersperns
+dns_zonemgr_managezone
+dns_zonemgr_releasezone
+dns_zonemgr_resumexfrs
+dns_zonemgr_setiolimit
+dns_zonemgr_setnotifyrate
+dns_zonemgr_setserialqueryrate
+dns_zonemgr_setsize
+dns_zonemgr_setstartupnotifyrate
+dns_zonemgr_settransfersin
+dns_zonemgr_settransfersperns
+dns_zonemgr_shutdown
+dns_zonemgr_unreachable
+dns_zonemgr_unreachableadd
+dns_zonemgr_unreachabledel
+dns_zt_apply
+dns_zt_apply2
+dns_zt_asyncload
+dns_zt_asyncload2
+dns_zt_attach
+dns_zt_create
+dns_zt_detach
+dns_zt_find
+dns_zt_flushanddetach
+dns_zt_freezezones
+dns_zt_load
+dns_zt_loadnew
+dns_zt_mount
+dns_zt_setviewcommit
+dns_zt_setviewrevert
+dns_zt_unmount
+dst_algorithm_supported
+dst_context_adddata
+dst_context_create
+dst_context_create2
+dst_context_create3
+dst_context_create4
+dst_context_destroy
+dst_context_sign
+dst_context_verify
+dst_context_verify2
+dst_ds_digest_supported
+dst_gssapi_acceptctx
+dst_gssapi_acquirecred
+dst_gssapi_deletectx
+dst_gssapi_identitymatchesrealmkrb5
+dst_gssapi_identitymatchesrealmms
+dst_gssapi_initctx
+dst_gssapi_releasecred
+dst_key_alg
+dst_key_attach
+dst_key_buildfilename
+dst_key_buildinternal
+dst_key_class
+dst_key_compare
+dst_key_computesecret
+dst_key_dump
+dst_key_flags
+dst_key_format
+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_generate2
+dst_key_getbits
+dst_key_getfilename
+dst_key_getgssctx
+dst_key_getnum
+dst_key_getprivateformat
+dst_key_gettime
+dst_key_getttl
+dst_key_id
+dst_key_inactive
+dst_key_isexternal
+dst_key_isnullkey
+dst_key_isprivate
+dst_key_iszonekey
+dst_key_name
+dst_key_paramcompare
+dst_key_privatefrombuffer
+dst_key_proto
+dst_key_pubcompare
+dst_key_read_public
+dst_key_restore
+dst_key_rid
+dst_key_secretsize
+dst_key_setbits
+dst_key_setexternal
+dst_key_setflags
+dst_key_setinactive
+dst_key_setnum
+dst_key_setprivateformat
+dst_key_settime
+dst_key_setttl
+dst_key_sigsize
+dst_key_size
+dst_key_tkeytoken
+dst_key_tobuffer
+dst_key_todns
+dst_key_tofile
+dst_key_unsetnum
+dst_key_unsettime
+dst_lib_destroy
+dst_lib_init
+dst_lib_init2
+dst_lib_initmsgcat
+dst_region_computeid
+dst_region_computerid
+dst_result_register
+dst_result_totext
+@IF NOLONGER
+; Exported Data
+
+EXPORTS
+
+dns_pps DATA
+dns_master_style_full DATA
+dns_msgcat DATA
+dns_tsig_hmacmd5_name DATA
+dns_zone_mkey_day DATA
+dns_zone_mkey_hour DATA
+dns_zone_mkey_month DATA
+dst__memory_pool DATA
+dst_msgcat DATA
+@END NOLONGER
diff --git a/lib/dns/win32/libdns.dsp.in b/lib/dns/win32/libdns.dsp.in
new file mode 100644
index 0000000..cc960fe
--- /dev/null
+++ b/lib/dns/win32/libdns.dsp.in
@@ -0,0 +1,937 @@
+# Microsoft Developer Studio Project File - Name="libdns" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "@PLATFORM@ (x86) Dynamic-Link Library" 0x0102
+
+CFG=libdns - @PLATFORM@ 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 - @PLATFORM@ Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "libdns - @PLATFORM@ Release" (based on "@PLATFORM@ (x86) Dynamic-Link Library")
+!MESSAGE "libdns - @PLATFORM@ Debug" (based on "@PLATFORM@ (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 - @PLATFORM@ 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 @COPTX@ @COPTI@ /O2 /D "BIND9" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "libdns_EXPORTS" @COPTY@ /FD /c
+# ADD CPP /nologo /MD /W3 @COPTX@ @COPTI@ /O2 /I "./" /I "../../../" /I "include" /I "../include" /I "../../isc/win32" /I "../../isc/win32/include" /I "../../isc/include" @LIBXML2_INC@ @OPENSSL_INC@ @GSSAPI_INC@ @GEOIP_INC@ /D "NDEBUG" /D "BIND9" /D "WIN32" /D "_WINDOWS" /D "__STDC__" /D "_MBCS" /D "_USRDLL" /D "USE_MD5" @CRYPTO@ @USE_GSSAPI@ @USE_ISC_SPNEGO@ /D "LIBDNS_EXPORTS" @COPTY@ /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@
+# ADD LINK32 @LIBXML2_LIB@ user32.lib advapi32.lib ws2_32.lib ../../isc/win32/Release/libisc.lib @OPENSSL_LIB@ @GSSAPI_LIB@ @KRB5_LIB@ @GEOIP_LIB@ /nologo /dll @MACHINE@ /out:"../../../Build/Release/libdns.dll"
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ 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 @COPTX@ @COPTI@ /ZI /Od /D "BIND9" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "libdns_EXPORTS" @COPTY@ /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm @COPTX@ @COPTI@ /ZI /Od /I "./" /I "../../../" /I "include" /I "../include" /I "../../isc/win32" /I "../../isc/win32/include" /I "../../isc/include" @OPENSSL_INC@ @LIBXML2_INC@ @GSSAPI_INC@ @GEOIP_INC@ /D "_DEBUG" /D "BIND9" /D "WIN32" /D "_WINDOWS" /D "__STDC__" /D "_MBCS" /D "_USRDLL" /D "USE_MD5" @CRYPTO@ @USE_GSSAPI@ @USE_ISC_SPNEGO@ /D "LIBDNS_EXPORTS" /FR @COPTY@ /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@ /pdbtype:sept
+# ADD LINK32 @LIBXML2_LIB@ user32.lib advapi32.lib ws2_32.lib ../../isc/win32/debug/libisc.lib @OPENSSL_LIB@ @GSSAPI_LIB@ @KRB5_LIB@ @GEOIP_LIB@ /nologo /dll /map /debug @MACHINE@ /out:"../../../Build/Debug/libdns.dll" /pdbtype:sept
+
+!ENDIF
+
+# Begin Target
+
+# Name "libdns - @PLATFORM@ Release"
+# Name "libdns - @PLATFORM@ 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\badcache.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\catz.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\cert.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\client.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\clientinfo.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\dns64.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\dnssec.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\dnstap.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\ds.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\dyndb.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\ecdb.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\edns.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
+@IF GEOIP
+# Begin Source File
+
+SOURCE=..\include\dns\geoip.h
+# End Source File
+@END GEOIP
+# Begin Source File
+
+SOURCE=..\include\dns\ipkeylist.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\keydata.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\nta.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\private.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=..\rdatalist_p.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\rpz.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\rrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\rriterator.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\tsec.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\update.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=..\badcache.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=..\catz.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\client.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\clientinfo.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=..\dns64.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=..\dyndb.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\ecdb.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\forward.c
+# End Source File
+@IF GEOIP
+# Begin Source File
+
+SOURCE=..\geoip.c
+# End Source File
+@END GEOIP
+# Begin Source File
+
+SOURCE=..\ipkeylist.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=..\keydata.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=..\nta.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=..\private.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=..\rpz.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\rrl.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\rriterator.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=..\ssu_external.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=..\tsec.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=..\update.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
+@IF OPENSSL
+# 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=..\opensslecdsa_link.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\openssleddsa_link.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\opensslgost_link.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\opensslrsa_link.c
+# End Source File
+@END OPENSSL
+@IF PKCS11
+# Begin Source File
+
+SOURCE=..\pkcs11.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\pkcs11dh_link.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\pkcs11dsa_link.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\pkcs11ecdsa_link.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\pkcs11eddsa_link.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\pkcs11gost_link.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\pkcs11rsa_link.c
+# End Source File
+@END PKCS11
+# 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.in b/lib/dns/win32/libdns.mak.in
new file mode 100644
index 0000000..df8666a
--- /dev/null
+++ b/lib/dns/win32/libdns.mak.in
@@ -0,0 +1,2890 @@
+# Microsoft Developer Studio Generated NMAKE File, Based on libdns.dsp
+!IF "$(CFG)" == ""
+CFG=libdns - @PLATFORM@ Debug
+!MESSAGE No configuration specified. Defaulting to libdns - @PLATFORM@ Debug.
+!ENDIF
+
+!IF "$(CFG)" != "libdns - @PLATFORM@ Release" && "$(CFG)" != "libdns - @PLATFORM@ 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 - @PLATFORM@ Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "libdns - @PLATFORM@ Release" (based on "@PLATFORM@ (x86) Dynamic-Link Library")
+!MESSAGE "libdns - @PLATFORM@ Debug" (based on "@PLATFORM@ (x86) Dynamic-Link Library")
+!MESSAGE
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ 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 - @PLATFORM@ Release"
+
+OUTDIR=.\Release
+INTDIR=.\Release
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "..\..\..\Build\Release\libdns.dll"
+
+!ELSE
+
+ALL : "libisc - @PLATFORM@ Release" "..\..\..\Build\Release\libdns.dll"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libisc - @PLATFORM@ ReleaseCLEAN"
+!ELSE
+CLEAN :
+!ENDIF
+ -@erase "$(INTDIR)\acache.obj"
+ -@erase "$(INTDIR)\acl.obj"
+ -@erase "$(INTDIR)\adb.obj"
+ -@erase "$(INTDIR)\badcache.obj"
+ -@erase "$(INTDIR)\byaddr.obj"
+ -@erase "$(INTDIR)\cache.obj"
+ -@erase "$(INTDIR)\callbacks.obj"
+ -@erase "$(INTDIR)\client.obj"
+ -@erase "$(INTDIR)\clientinfo.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)\dns64.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)\dyndb.obj"
+ -@erase "$(INTDIR)\ecdb.obj"
+ -@erase "$(INTDIR)\forward.obj"
+@IF GEOIP
+ -@erase "$(INTDIR)\geoip.obj"
+@END GEOIP
+ -@erase "$(INTDIR)\gssapi_link.obj"
+ -@erase "$(INTDIR)\gssapictx.obj"
+ -@erase "$(INTDIR)\spnego.obj"
+ -@erase "$(INTDIR)\hmac_link.obj"
+ -@erase "$(INTDIR)\ipkeylist.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)\nta.obj"
+@IF OPENSSL
+ -@erase "$(INTDIR)\openssl_link.obj"
+ -@erase "$(INTDIR)\openssldh_link.obj"
+ -@erase "$(INTDIR)\openssldsa_link.obj"
+ -@erase "$(INTDIR)\opensslecdsa_link.obj"
+ -@erase "$(INTDIR)\openssleddsa_link.obj"
+ -@erase "$(INTDIR)\opensslgost_link.obj"
+ -@erase "$(INTDIR)\opensslrsa_link.obj"
+@END OPENSSL
+ -@erase "$(INTDIR)\order.obj"
+ -@erase "$(INTDIR)\peer.obj"
+@IF PKCS11
+ -@erase "$(INTDIR)\pkcs11.obj"
+ -@erase "$(INTDIR)\pkcs11dh_link.obj"
+ -@erase "$(INTDIR)\pkcs11dsa_link.obj"
+ -@erase "$(INTDIR)\pkcs11ecdsa_link.obj"
+ -@erase "$(INTDIR)\pkcs11eddsa_link.obj"
+ -@erase "$(INTDIR)\pkcs11gost_link.obj"
+ -@erase "$(INTDIR)\pkcs11rsa_link.obj"
+@END PKCS11
+ -@erase "$(INTDIR)\portlist.obj"
+ -@erase "$(INTDIR)\private.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)\rpz.obj"
+ -@erase "$(INTDIR)\rrl.obj"
+ -@erase "$(INTDIR)\sdb.obj"
+ -@erase "$(INTDIR)\sdlz.obj"
+ -@erase "$(INTDIR)\soa.obj"
+ -@erase "$(INTDIR)\ssu.obj"
+ -@erase "$(INTDIR)\ssu_external.obj"
+ -@erase "$(INTDIR)\stats.obj"
+ -@erase "$(INTDIR)\tcpmsg.obj"
+ -@erase "$(INTDIR)\time.obj"
+ -@erase "$(INTDIR)\timer.obj"
+ -@erase "$(INTDIR)\tkey.obj"
+ -@erase "$(INTDIR)\tsec.obj"
+ -@erase "$(INTDIR)\tsig.obj"
+ -@erase "$(INTDIR)\ttl.obj"
+ -@erase "$(INTDIR)\update.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)"
+
+LIBXML=@LIBXML2_LIB@
+CPP=cl.exe
+CPP_PROJ=/nologo /MD /W3 @COPTX@ @COPTI@ /O2 /I "./" /I "../../../" /I "include" /I "../include" /I "../../isc/win32" /I "../../isc/win32/include" /I "../../isc/include" @OPENSSL_INC@ @LIBXML2_INC@ @GSSAPI_INC@ @GEOIP_INC@ /D "NDEBUG" /D "BIND9" /D "WIN32" /D "_WINDOWS" /D "__STDC__" /D "_MBCS" /D "_USRDLL" /D "USE_MD5" @CRYPTO@ @USE_GSSAPI@ @USE_ISC_SPNEGO@ /D "LIBDNS_EXPORTS" /Fp"$(INTDIR)\libdns.pch" @COPTY@ /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 $(LIBXML) ../../isc/win32/Release/libisc.lib @OPENSSL_LIB@ @GSSAPI_LIB@ @KRB5_LIB@ @GEOIP_LIB@ /nologo /dll /incremental:no /pdb:"$(OUTDIR)\libdns.pdb" @MACHINE@ /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)\badcache.obj" \
+ "$(INTDIR)\byaddr.obj" \
+ "$(INTDIR)\cache.obj" \
+ "$(INTDIR)\callbacks.obj" \
+ "$(INTDIR)\client.obj" \
+ "$(INTDIR)\clientinfo.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)\dns64.obj" \
+ "$(INTDIR)\dnssec.obj" \
+ "$(INTDIR)\ds.obj" \
+ "$(INTDIR)\dyndb.obj" \
+ "$(INTDIR)\ecdb.obj" \
+ "$(INTDIR)\forward.obj" \
+@IF GEOIP
+ "$(INTDIR)\geoip.obj" \
+@END GEOIP
+ "$(INTDIR)\ipkeylist.obj" \
+ "$(INTDIR)\iptable.obj" \
+ "$(INTDIR)\journal.obj" \
+ "$(INTDIR)\keydata.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)\nta.obj" \
+ "$(INTDIR)\order.obj" \
+ "$(INTDIR)\peer.obj" \
+ "$(INTDIR)\portlist.obj" \
+ "$(INTDIR)\private.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)\rpz.obj" \
+ "$(INTDIR)\rrl.obj" \
+ "$(INTDIR)\rriterator.obj" \
+ "$(INTDIR)\sdb.obj" \
+ "$(INTDIR)\sdlz.obj" \
+ "$(INTDIR)\soa.obj" \
+ "$(INTDIR)\ssu.obj" \
+ "$(INTDIR)\ssu_external.obj" \
+ "$(INTDIR)\stats.obj" \
+ "$(INTDIR)\tcpmsg.obj" \
+ "$(INTDIR)\time.obj" \
+ "$(INTDIR)\timer.obj" \
+ "$(INTDIR)\tkey.obj" \
+ "$(INTDIR)\tsec.obj" \
+ "$(INTDIR)\tsig.obj" \
+ "$(INTDIR)\ttl.obj" \
+ "$(INTDIR)\update.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" \
+@IF OPENSSL
+ "$(INTDIR)\openssl_link.obj" \
+ "$(INTDIR)\openssldh_link.obj" \
+ "$(INTDIR)\openssldsa_link.obj" \
+ "$(INTDIR)\opensslecdsa_link.obj" \
+ "$(INTDIR)\openssleddsa_link.obj" \
+ "$(INTDIR)\opensslgost_link.obj" \
+ "$(INTDIR)\opensslrsa_link.obj" \
+@END OPENSSL
+@IF PKCS11
+ "$(INTDIR)\pkcs11.obj" \
+ "$(INTDIR)\pkcs11dh_link.obj" \
+ "$(INTDIR)\pkcs11dsa_link.obj" \
+ "$(INTDIR)\pkcs11ecdsa_link.obj" \
+ "$(INTDIR)\pkcs11eddsa_link.obj" \
+ "$(INTDIR)\pkcs11gost_link.obj" \
+ "$(INTDIR)\pkcs11rsa_link.obj" \
+@END PKCS11
+ "..\..\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 - @PLATFORM@ 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 - @PLATFORM@ Debug" "..\..\..\Build\Debug\libdns.dll" "$(OUTDIR)\libdns.bsc"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libisc - @PLATFORM@ 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)\badcache.obj"
+ -@erase "$(INTDIR)\badcache.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)\client.obj"
+ -@erase "$(INTDIR)\client.sbr"
+ -@erase "$(INTDIR)\clientinfo.obj"
+ -@erase "$(INTDIR)\clientinfo.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)\dns64.obj"
+ -@erase "$(INTDIR)\dns64.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)\dyndb.obj"
+ -@erase "$(INTDIR)\dyndb.sbr"
+ -@erase "$(INTDIR)\ecdb.obj"
+ -@erase "$(INTDIR)\ecdb.sbr"
+ -@erase "$(INTDIR)\forward.obj"
+ -@erase "$(INTDIR)\forward.sbr"
+@IF GEOIP
+ -@erase "$(INTDIR)\geoip.obj"
+ -@erase "$(INTDIR)\geoip.sbr"
+@END GEOIP
+ -@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)\ipkeylist.obj"
+ -@erase "$(INTDIR)\ipkeylist.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)\keydata.obj"
+ -@erase "$(INTDIR)\keydata.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)\nta.obj"
+ -@erase "$(INTDIR)\nta.sbr"
+@IF OPENSSL
+ -@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)\opensslecdsa_link.obj"
+ -@erase "$(INTDIR)\opensslecdsa_link.sbr"
+ -@erase "$(INTDIR)\openssleddsa_link.obj"
+ -@erase "$(INTDIR)\openssleddsa_link.sbr"
+ -@erase "$(INTDIR)\opensslgost_link.obj"
+ -@erase "$(INTDIR)\opensslgost_link.sbr"
+ -@erase "$(INTDIR)\opensslrsa_link.obj"
+ -@erase "$(INTDIR)\opensslrsa_link.sbr"
+@END OPENSSL
+ -@erase "$(INTDIR)\order.obj"
+ -@erase "$(INTDIR)\order.sbr"
+ -@erase "$(INTDIR)\peer.obj"
+ -@erase "$(INTDIR)\peer.sbr"
+@IF PKCS11
+ -@erase "$(INTDIR)\pkcs11.obj"
+ -@erase "$(INTDIR)\pkcs11.sbr"
+ -@erase "$(INTDIR)\pkcs11dh_link.obj"
+ -@erase "$(INTDIR)\pkcs11dh_link.sbr"
+ -@erase "$(INTDIR)\pkcs11dsa_link.obj"
+ -@erase "$(INTDIR)\pkcs11dsa_link.sbr"
+ -@erase "$(INTDIR)\pkcs11ecdsa_link.obj"
+ -@erase "$(INTDIR)\pkcs11ecdsa_link.sbr"
+ -@erase "$(INTDIR)\pkcs11eddsa_link.obj"
+ -@erase "$(INTDIR)\pkcs11eddsa_link.sbr"
+ -@erase "$(INTDIR)\pkcs11gost_link.obj"
+ -@erase "$(INTDIR)\pkcs11gost_link.sbr"
+ -@erase "$(INTDIR)\pkcs11rsa_link.obj"
+ -@erase "$(INTDIR)\pkcs11rsa_link.sbr"
+@END PKCS11
+ -@erase "$(INTDIR)\portlist.obj"
+ -@erase "$(INTDIR)\portlist.sbr"
+ -@erase "$(INTDIR)\private.obj"
+ -@erase "$(INTDIR)\private.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)\rpz.obj"
+ -@erase "$(INTDIR)\rpz.sbr"
+ -@erase "$(INTDIR)\rrl.obj"
+ -@erase "$(INTDIR)\rrl.sbr"
+ -@erase "$(INTDIR)\rriterator.obj"
+ -@erase "$(INTDIR)\rriterator.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_external.obj"
+ -@erase "$(INTDIR)\ssu.sbr"
+ -@erase "$(INTDIR)\ssu_external.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)\tsec.obj"
+ -@erase "$(INTDIR)\tsec.sbr"
+ -@erase "$(INTDIR)\tsig.obj"
+ -@erase "$(INTDIR)\tsig.sbr"
+ -@erase "$(INTDIR)\ttl.obj"
+ -@erase "$(INTDIR)\ttl.sbr"
+ -@erase "$(INTDIR)\update.obj"
+ -@erase "$(INTDIR)\update.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 @COPTX@ @COPTI@ /ZI /Od /I "./" /I "../../../" /I "include" /I "../include" /I "../../isc/win32" /I "../../isc/win32/include" /I "../../isc/include" @OPENSSL_INC@ @LIBXML2_INC@ @GSSAPI_INC@ @GEOIP_INC@ /D "_DEBUG" /D "BIND9" /D "WIN32" /D "_WINDOWS" /D "__STDC__" /D "_MBCS" /D "_USRDLL" /D "USE_MD5" @CRYPTO@ @USE_GSSAPI@ @USE_ISC_SPNEGO@ /D "LIBDNS_EXPORTS" /FR"$(INTDIR)\\" /Fp"$(INTDIR)\libdns.pch" @COPTY@ /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)\badcache.sbr" \
+ "$(INTDIR)\byaddr.sbr" \
+ "$(INTDIR)\cache.sbr" \
+ "$(INTDIR)\callbacks.sbr" \
+ "$(INTDIR)\client.sbr" \
+ "$(INTDIR)\clientinfo.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)\dns64.sbr" \
+ "$(INTDIR)\dnssec.sbr" \
+ "$(INTDIR)\ds.sbr" \
+ "$(INTDIR)\dyndb.sbr" \
+ "$(INTDIR)\ecdb.sbr" \
+ "$(INTDIR)\forward.sbr" \
+@IF GEOIP
+ "$(INTDIR)\geoip.sbr" \
+@END GEOIP
+ "$(INTDIR)\ipkeylist.sbr" \
+ "$(INTDIR)\iptable.sbr" \
+ "$(INTDIR)\journal.sbr" \
+ "$(INTDIR)\keydata.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)\nta.sbr" \
+ "$(INTDIR)\order.sbr" \
+ "$(INTDIR)\peer.sbr" \
+ "$(INTDIR)\portlist.sbr" \
+ "$(INTDIR)\private.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)\rpz.sbr" \
+ "$(INTDIR)\rrl.sbr" \
+ "$(INTDIR)\rriterator.sbr" \
+ "$(INTDIR)\sdb.sbr" \
+ "$(INTDIR)\sdlz.sbr" \
+ "$(INTDIR)\soa.sbr" \
+ "$(INTDIR)\ssu.sbr" \
+ "$(INTDIR)\ssu_external.sbr" \
+ "$(INTDIR)\stats.sbr" \
+ "$(INTDIR)\tcpmsg.sbr" \
+ "$(INTDIR)\time.sbr" \
+ "$(INTDIR)\timer.sbr" \
+ "$(INTDIR)\tkey.sbr" \
+ "$(INTDIR)\tsec.sbr" \
+ "$(INTDIR)\tsig.sbr" \
+ "$(INTDIR)\ttl.sbr" \
+ "$(INTDIR)\update.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" \
+@IF OPENSSL
+ "$(INTDIR)\openssl_link.sbr" \
+ "$(INTDIR)\openssldh_link.sbr" \
+ "$(INTDIR)\openssldsa_link.sbr" \
+ "$(INTDIR)\opensslecdsa_link.sbr" \
+ "$(INTDIR)\openssleddsa_link.sbr" \
+ "$(INTDIR)\opensslgost_link.sbr" \
+ "$(INTDIR)\opensslrsa_link.sbr" \
+@END OPENSSL
+@IF PKCS11
+ "$(INTDIR)\pkcs11.sbr" \
+ "$(INTDIR)\pkcs11dh_link.sbr" \
+ "$(INTDIR)\pkcs11dsa_link.sbr" \
+ "$(INTDIR)\pkcs11ecdsa_link.sbr" \
+ "$(INTDIR)\pkcs11eddsa_link.sbr" \
+ "$(INTDIR)\pkcs11gost_link.sbr" \
+ "$(INTDIR)\pkcs11rsa_link.sbr"
+@END PKCS11
+
+"$(OUTDIR)\libdns.bsc" : "$(OUTDIR)" $(BSC32_SBRS)
+ $(BSC32) @<<
+ $(BSC32_FLAGS) $(BSC32_SBRS)
+<<
+
+LINK32=link.exe
+LINK32_FLAGS=user32.lib advapi32.lib ws2_32.lib $(LIBXML) ../../isc/win32/Debug/libisc.lib @OPENSSL_LIB@ @GSSAPI_LIB@ @KRB5_LIB@ @GEOIP_LIB@ @GEOIP_LIB@ /nologo /dll /incremental:yes /pdb:"$(OUTDIR)\libdns.pdb" /map:"$(INTDIR)\libdns.map" /debug @MACHINE@ /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)\badcache.obj" \
+ "$(INTDIR)\byaddr.obj" \
+ "$(INTDIR)\cache.obj" \
+ "$(INTDIR)\callbacks.obj" \
+ "$(INTDIR)\client.obj" \
+ "$(INTDIR)\clientinfo.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)\dns64.obj" \
+ "$(INTDIR)\dnssec.obj" \
+ "$(INTDIR)\ds.obj" \
+ "$(INTDIR)\dyndb.obj" \
+ "$(INTDIR)\ecdb.obj" \
+ "$(INTDIR)\forward.obj" \
+@IF GEOIP
+ "$(INTDIR)\geoip.obj" \
+@END GEOIP
+ "$(INTDIR)\ipkeylist.obj" \
+ "$(INTDIR)\iptable.obj" \
+ "$(INTDIR)\journal.obj" \
+ "$(INTDIR)\key.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)\nta.obj" \
+ "$(INTDIR)\order.obj" \
+ "$(INTDIR)\peer.obj" \
+ "$(INTDIR)\portlist.obj" \
+ "$(INTDIR)\private.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)\rpz.obj" \
+ "$(INTDIR)\rrl.obj" \
+ "$(INTDIR)\rriterator.obj" \
+ "$(INTDIR)\sdb.obj" \
+ "$(INTDIR)\sdlz.obj" \
+ "$(INTDIR)\soa.obj" \
+ "$(INTDIR)\ssu.obj" \
+ "$(INTDIR)\ssu_external.obj" \
+ "$(INTDIR)\stats.obj" \
+ "$(INTDIR)\tcpmsg.obj" \
+ "$(INTDIR)\time.obj" \
+ "$(INTDIR)\timer.obj" \
+ "$(INTDIR)\tkey.obj" \
+ "$(INTDIR)\tsec.obj" \
+ "$(INTDIR)\tsig.obj" \
+ "$(INTDIR)\ttl.obj" \
+ "$(INTDIR)\update.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" \
+@IF OPENSSL
+ "$(INTDIR)\openssl_link.obj" \
+ "$(INTDIR)\openssldh_link.obj" \
+ "$(INTDIR)\openssldsa_link.obj" \
+ "$(INTDIR)\opensslecdsa_link.obj" \
+ "$(INTDIR)\openssleddsa_link.obj" \
+ "$(INTDIR)\opensslgost_link.obj" \
+ "$(INTDIR)\opensslrsa_link.obj" \
+@END OPENSSL
+@IF PKCS11
+ "$(INTDIR)\pkcs11.obj" \
+ "$(INTDIR)\pkcs11dh_link.obj" \
+ "$(INTDIR)\pkcs11dsa_link.obj" \
+ "$(INTDIR)\pkcs11ecdsa_link.obj" \
+ "$(INTDIR)\pkcs11eddsa_link.obj" \
+ "$(INTDIR)\pkcs11gost_link.obj" \
+ "$(INTDIR)\pkcs11rsa_link.obj" \
+@END PKCS11
+ "..\..\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 - @PLATFORM@ Release" || "$(CFG)" == "libdns - @PLATFORM@ Debug"
+SOURCE=..\acache.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\acache.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\acache.obj" "$(INTDIR)\acache.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\acl.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\acl.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\acl.obj" "$(INTDIR)\acl.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\adb.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\adb.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\adb.obj" "$(INTDIR)\adb.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\badcache.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\badcache.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\badcache.obj" "$(INTDIR)\badcache.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\byaddr.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\byaddr.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\byaddr.obj" "$(INTDIR)\byaddr.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\cache.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\cache.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\cache.obj" "$(INTDIR)\cache.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\callbacks.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\callbacks.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\callbacks.obj" "$(INTDIR)\callbacks.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\catz.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\catz.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\catz.obj" "$(INTDIR)\catz.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\client.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\client.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\client.obj" "$(INTDIR)\client.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\clientinfo.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\clientinfo.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\clientinfo.obj" "$(INTDIR)\clientinfo.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\compress.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\compress.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\compress.obj" "$(INTDIR)\compress.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\db.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\db.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\db.obj" "$(INTDIR)\db.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\dbiterator.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\dbiterator.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\dbiterator.obj" "$(INTDIR)\dbiterator.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\dbtable.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\dbtable.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\dbtable.obj" "$(INTDIR)\dbtable.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\diff.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\diff.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\diff.obj" "$(INTDIR)\diff.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\dispatch.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+CPP_SWITCHES=/nologo /MD /W3 @COPTX@ @COPTI@ /O2 /I "./" /I "../../../" /I "include" /I "../include" /I "../../isc/win32" /I "../../isc/win32/include" /I "../../isc/include" @OPENSSL_INC@ @LIBXML2_INC@ @GSSAPI_INC@ @GEOIP_INC@ /D "NDEBUG" /D "BIND9" /D "WIN32" /D "_WINDOWS" /D "__STDC__" /D "_MBCS" /D "_USRDLL" /D "USE_MD5" @CRYPTO@ @USE_GSSAPI@ @USE_ISC_SPNEGO@ /D "LIBDNS_EXPORTS" /Fp"$(INTDIR)\libdns.pch" @COPTY@ /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c
+
+"$(INTDIR)\dispatch.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) @<<
+ $(CPP_SWITCHES) $(SOURCE)
+<<
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+CPP_SWITCHES=/nologo /MDd /W3 /Gm @COPTX@ @COPTI@ /ZI /Od /I "./" /I "../../../" /I "include" /I "../include" /I "../../isc/win32" /I "../../isc/win32/include" /I "../../isc/include" @OPENSSL_INC@ @LIBXML2_INC@ @GSSAPI_INC@ @GEOIP_INC@ /D "_DEBUG" /D "BIND9" /D "WIN32" /D "_WINDOWS" /D "__STDC__" /D "_MBCS" /D "_USRDLL" /D "USE_MD5" @CRYPTO@ @USE_GSSAPI@ @USE_ISC_SPNEGO@ /D "LIBDNS_EXPORTS" /FR"$(INTDIR)\\" /Fp"$(INTDIR)\libdns.pch" @COPTY@ /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 - @PLATFORM@ Release"
+
+
+"$(INTDIR)\dlz.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+"$(INTDIR)\dlz.obj" "$(INTDIR)\dlz.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=.\DLLMain.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\DLLMain.obj" : $(SOURCE) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\DLLMain.obj" "$(INTDIR)\DLLMain.sbr" : $(SOURCE) "$(INTDIR)"
+
+
+!ENDIF
+
+SOURCE=..\dns64.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\dns64.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\dns64.obj" "$(INTDIR)\dns64.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\dnssec.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\dnssec.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\dnssec.obj" "$(INTDIR)\dnssec.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\ds.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\ds.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\ds.obj" "$(INTDIR)\ds.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\dyndb.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\dyndb.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\dyndb.obj" "$(INTDIR)\dyndb.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\ecdb.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\ecdb.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\ecdb.obj" "$(INTDIR)\ecdb.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\forward.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\forward.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\forward.obj" "$(INTDIR)\forward.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+@IF GEOIP
+SOURCE=..\geoip.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\geoip.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\geoip.obj" "$(INTDIR)\geoip.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+@END GEOIP
+
+SOURCE=..\ipkeylist.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\ipkeylist.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\ipkeylist.obj" "$(INTDIR)\ipkeylist.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\iptable.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\iptable.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\iptable.obj" "$(INTDIR)\iptable.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\journal.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\journal.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\journal.obj" "$(INTDIR)\journal.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\keydata.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\keydata.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\keydata.obj" "$(INTDIR)\keydata.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\keytable.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\keytable.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\keytable.obj" "$(INTDIR)\keytable.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\lib.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\lib.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\lib.obj" "$(INTDIR)\lib.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\log.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\log.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\log.obj" "$(INTDIR)\log.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\lookup.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\lookup.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\lookup.obj" "$(INTDIR)\lookup.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\master.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\master.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\master.obj" "$(INTDIR)\master.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\masterdump.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\masterdump.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\masterdump.obj" "$(INTDIR)\masterdump.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\message.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\message.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\message.obj" "$(INTDIR)\message.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\name.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\name.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\name.obj" "$(INTDIR)\name.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\ncache.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\ncache.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\ncache.obj" "$(INTDIR)\ncache.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\nsec.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\nsec.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\nsec.obj" "$(INTDIR)\nsec.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\nsec3.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\nsec3.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\nsec3.obj" "$(INTDIR)\nsec3.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\nta.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\nta.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\nta.obj" "$(INTDIR)\nta.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\order.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\order.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\order.obj" "$(INTDIR)\order.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\peer.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\peer.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\peer.obj" "$(INTDIR)\peer.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+
+SOURCE=..\portlist.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\portlist.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\portlist.obj" "$(INTDIR)\portlist.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+
+SOURCE=..\private.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\private.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\private.obj" "$(INTDIR)\portlist.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\rbt.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\rbt.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\rbt.obj" "$(INTDIR)\rbt.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\rbtdb.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\rbtdb.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\rbtdb.obj" "$(INTDIR)\rbtdb.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\rbtdb64.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\rbtdb64.obj" : $(SOURCE) "$(INTDIR)" "..\rbtdb.c"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\rbtdb64.obj" "$(INTDIR)\rbtdb64.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\rcode.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\rcode.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\rcode.obj" "$(INTDIR)\rcode.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\rdata.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\rdata.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\rdata.obj" "$(INTDIR)\rdata.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\rdatalist.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\rdatalist.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\rdatalist.obj" "$(INTDIR)\rdatalist.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\rdataset.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\rdataset.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\rdataset.obj" "$(INTDIR)\rdataset.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\rdatasetiter.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\rdatasetiter.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\rdatasetiter.obj" "$(INTDIR)\rdatasetiter.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\rdataslab.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\rdataslab.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\rdataslab.obj" "$(INTDIR)\rdataslab.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\request.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\request.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\request.obj" "$(INTDIR)\request.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\resolver.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\resolver.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\resolver.obj" "$(INTDIR)\resolver.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\result.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\result.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\result.obj" "$(INTDIR)\result.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\rootns.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\rootns.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\rootns.obj" "$(INTDIR)\rootns.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\rpz.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\rpz.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\rpz.obj" "$(INTDIR)\rpz.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\rrl.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\rrl.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\rrl.obj" "$(INTDIR)\rrl.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\rriterator.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\rriterator.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\rriterator.obj" "$(INTDIR)\rriterator.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\sdb.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\sdb.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\sdb.obj" "$(INTDIR)\sdb.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\sdlz.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\sdlz.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\sdlz.obj" "$(INTDIR)\sdlz.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\soa.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\soa.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\soa.obj" "$(INTDIR)\soa.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\ssu.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\ssu.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\ssu.obj" "$(INTDIR)\ssu.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\ssu_external.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\ssu_external.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\ssu_external.obj" "$(INTDIR)\ssu_external.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\stats.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\stats.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\stats.obj" "$(INTDIR)\stats.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\tcpmsg.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\tcpmsg.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\tcpmsg.obj" "$(INTDIR)\tcpmsg.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\time.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\time.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\time.obj" "$(INTDIR)\time.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\timer.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\timer.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\timer.obj" "$(INTDIR)\timer.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\tkey.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\tkey.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\tkey.obj" "$(INTDIR)\tkey.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\tsec.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\tsec.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\tsec.obj" "$(INTDIR)\tsec.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\tsig.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\tsig.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\tsig.obj" "$(INTDIR)\tsig.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\ttl.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\ttl.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\ttl.obj" "$(INTDIR)\ttl.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\update.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\update.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\update.obj" "$(INTDIR)\update.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\validator.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\validator.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\validator.obj" "$(INTDIR)\validator.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=.\version.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\version.obj" : $(SOURCE) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\version.obj" "$(INTDIR)\version.sbr" : $(SOURCE) "$(INTDIR)"
+
+
+!ENDIF
+
+SOURCE=..\view.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\view.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\view.obj" "$(INTDIR)\view.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\xfrin.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\xfrin.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\xfrin.obj" "$(INTDIR)\xfrin.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\zone.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\zone.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\zone.obj" "$(INTDIR)\zone.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\zonekey.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\zonekey.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\zonekey.obj" "$(INTDIR)\zonekey.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\zt.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\zt.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\zt.obj" "$(INTDIR)\zt.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\dst_api.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\dst_api.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\dst_api.obj" "$(INTDIR)\dst_api.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\dst_lib.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\dst_lib.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\dst_lib.obj" "$(INTDIR)\dst_lib.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\dst_parse.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\dst_parse.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\dst_parse.obj" "$(INTDIR)\dst_parse.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\dst_result.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\dst_result.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\dst_result.obj" "$(INTDIR)\dst_result.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\gssapi_link.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\gssapi_link.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\gssapi_link.obj" "$(INTDIR)\gssapi_link.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\gssapictx.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\gssapictx.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\gssapictx.obj" "$(INTDIR)\gssapictx.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\spnego.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\spnego.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\spnego.obj" "$(INTDIR)\spnego.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\hmac_link.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\hmac_link.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\hmac_link.obj" "$(INTDIR)\hmac_link.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\key.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\key.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\key.obj" "$(INTDIR)\key.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+@IF OPENSSL
+SOURCE=..\openssl_link.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\openssl_link.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\openssl_link.obj" "$(INTDIR)\openssl_link.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\openssldh_link.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\openssldh_link.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\openssldh_link.obj" "$(INTDIR)\openssldh_link.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\openssldsa_link.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\openssldsa_link.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\openssldsa_link.obj" "$(INTDIR)\openssldsa_link.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\opensslecdsa_link.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\opensslecdsa_link.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\opensslecdsa_link.obj" "$(INTDIR)\opensslecdsa_link.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\openssleddsa_link.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\openssleddsa_link.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\openssleddsa_link.obj" "$(INTDIR)\openssleddsa_link.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\opensslgost_link.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\opensslgost_link.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\opensslgost_link.obj" "$(INTDIR)\opensslgost_link.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\opensslrsa_link.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\opensslrsa_link.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\opensslrsa_link.obj" "$(INTDIR)\opensslrsa_link.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+@END OPENSSL
+
+@IF PKCS11
+SOURCE=..\pkcs11.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\pkcs11.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\pkcs11.obj" "$(INTDIR)\pkcs11.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\pkcs11dh_link.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\pkcs11dh_link.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\pkcs11dh_link.obj" "$(INTDIR)\pkcs11dh_link.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\pkcs11dsa_link.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\pkcs11dsa_link.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\pkcs11dsa_link.obj" "$(INTDIR)\pkcs11dsa_link.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\pkcs11ecdsa_link.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\pkcs11ecdsa_link.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\pkcs11ecdsa_link.obj" "$(INTDIR)\pkcs11ecdsa_link.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\pkcs11eddsa_link.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\pkcs11eddsa_link.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\pkcs11eddsa_link.obj" "$(INTDIR)\pkcs11eddsa_link.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\pkcs11gost_link.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\pkcs11gost_link.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\pkcs11gost_link.obj" "$(INTDIR)\pkcs11gost_link.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\pkcs11rsa_link.c
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+
+"$(INTDIR)\pkcs11rsa_link.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\pkcs11rsa_link.obj" "$(INTDIR)\pkcs11rsa_link.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+@END PKCS11
+
+!IF "$(CFG)" == "libdns - @PLATFORM@ Release"
+
+"libisc - @PLATFORM@ Release" :
+ cd "..\..\isc\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libisc.mak" CFG="libisc - @PLATFORM@ Release"
+ cd "..\..\dns\win32"
+
+"libisc - @PLATFORM@ ReleaseCLEAN" :
+ cd "..\..\isc\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libisc.mak" CFG="libisc - @PLATFORM@ Release" RECURSE=1 CLEAN
+ cd "..\..\dns\win32"
+
+!ELSEIF "$(CFG)" == "libdns - @PLATFORM@ Debug"
+
+"libisc - @PLATFORM@ Debug" :
+ cd "..\..\isc\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libisc.mak" CFG="libisc - @PLATFORM@ Debug"
+ cd "..\..\dns\win32"
+
+"libisc - @PLATFORM@ DebugCLEAN" :
+ cd "..\..\isc\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libisc.mak" CFG="libisc - @PLATFORM@ 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/libdns.vcxproj.filters.in b/lib/dns/win32/libdns.vcxproj.filters.in
new file mode 100644
index 0000000..5c51043
--- /dev/null
+++ b/lib/dns/win32/libdns.vcxproj.filters.in
@@ -0,0 +1,690 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+ </Filter>
+ <Filter Include="Library Header Files">
+ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+ </Filter>
+ <Filter Include="Dst Header Files">
+ <UniqueIdentifier>{c76276a2-cee5-4b70-bf37-e0f2ef1ae4d6}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Dst Source Files">
+ <UniqueIdentifier>{ae84c9c7-5da5-4c0e-9e53-bfc34a5825ae}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Library Source Files">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="libdns.def" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="DLLMain.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="version.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\acache.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\acl.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\adb.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\badcache.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\byaddr.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\cache.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\callbacks.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\catz.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\client.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\clientinfo.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\compress.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\db.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\dbiterator.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\dbtable.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\diff.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\dispatch.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\dlz.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\dns64.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\dnssec.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\ds.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\dyndb.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\ecdb.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\fixedname.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\forward.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+@IF GEOIP
+ <ClCompile Include="..\geoip.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+@END GEOIP
+ <ClCompile Include="..\ipkeylist.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\iptable.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\journal.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\keydata.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\keytable.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\lib.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\log.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\lookup.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\master.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\masterdump.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\message.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\name.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\ncache.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nsec.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nsec3.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nta.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\order.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\peer.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\portlist.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\private.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\rbt.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\rbtdb.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\rbtdb64.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\rcode.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\rdata.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\rdatalist.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\rdataset.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\rdatasetiter.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\rdataslab.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\request.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\resolver.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\result.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\rootns.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\rpz.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\rriterator.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\rrl.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\sdb.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\sdlz.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\soa.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\ssu.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\ssu_external.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\stats.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\tcpmsg.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\time.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\timer.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\tkey.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\tsec.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\tsig.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\ttl.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\update.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\validator.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\view.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\xfrin.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\zone.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\zonekey.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\zt.c">
+ <Filter>Library Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\dst_api.c">
+ <Filter>Dst Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\dst_lib.c">
+ <Filter>Dst Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\dst_parse.c">
+ <Filter>Dst Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\dst_result.c">
+ <Filter>Dst Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\gssapi_link.c">
+ <Filter>Dst Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\gssapictx.c">
+ <Filter>Dst Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\hmac_link.c">
+ <Filter>Dst Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\key.c">
+ <Filter>Dst Source Files</Filter>
+ </ClCompile>
+@IF OPENSSL
+ <ClCompile Include="..\openssl_link.c">
+ <Filter>Dst Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\openssldh_link.c">
+ <Filter>Dst Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\openssldsa_link.c">
+ <Filter>Dst Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\opensslecdsa_link.c">
+ <Filter>Dst Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\openssleddsa_link.c">
+ <Filter>Dst Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\opensslgost_link.c">
+ <Filter>Dst Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\opensslrsa_link.c">
+ <Filter>Dst Source Files</Filter>
+ </ClCompile>
+@END OPENSSL
+@IF PKCS11
+ <ClCompile Include="..\pkcs11.c">
+ <Filter>Dst Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\pkcs11dh_link.c">
+ <Filter>Dst Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\pkcs11dsa_link.c">
+ <Filter>Dst Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\pkcs11ecdsa_link.c">
+ <Filter>Dst Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\pkcs11eddsa_link.c">
+ <Filter>Dst Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\pkcs11gost_link.c">
+ <Filter>Dst Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\pkcs11rsa_link.c">
+ <Filter>Dst Source Files</Filter>
+ </ClCompile>
+@END PKCS11
+ <ClCompile Include="..\spnego.c">
+ <Filter>Dst Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\code.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\rbtdb.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\rbtdb64.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\rdatalist_p.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\acache.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\acl.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\adb.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\badcache.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\bit.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\byaddr.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\cache.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\callbacks.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\catz.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\cert.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\client.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\clientinfo.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\compress.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\db.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\dbiterator.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\dbtable.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\diff.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\dispatch.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\dlz.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\dns64.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\dnssec.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\dnstap.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\ds.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\dsdigest.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\dyndb.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\ecdb.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\edns.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\enumclass.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\enumtype.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\events.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\fixedname.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\forward.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+@IF GEOIP
+ <ClInclude Include="..\include\dns\geoip.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+@END GEOIP
+ <ClInclude Include="..\include\dns\ipkeylist.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\iptable.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\journal.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\keydata.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\keyflags.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\keytable.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\keyvalues.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\lib.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\log.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\lookup.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\master.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\masterdump.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\message.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\name.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\ncache.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\nsec.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\nsec3.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\nta.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\opcode.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\order.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\peer.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\portlist.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\private.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\rbt.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\rcode.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\rdata.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\rdataclass.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\rdatalist.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\rdataset.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\rdatasetiter.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\rdataslab.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\rdatastruct.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\rdatatype.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\request.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\resolver.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\result.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\rootns.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\rpz.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\rriterator.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\rrl.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\sdb.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\sdlz.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\secalg.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\secproto.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\soa.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\ssu.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\stats.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\tcpmsg.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\time.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\timer.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\tkey.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\tsec.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\tsig.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\ttl.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\types.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\update.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\validator.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\version.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\view.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\xfrin.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\zone.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\zonekey.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dns\zt.h">
+ <Filter>Library Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dst\dst.h">
+ <Filter>Dst Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dst\gssapi.h">
+ <Filter>Dst Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dst\lib.h">
+ <Filter>Dst Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\dst\result.h">
+ <Filter>Dst Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\dst_gost.h">
+ <Filter>Dst Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\dst_internal.h">
+ <Filter>Dst Header Files</Filter>
+ </ClInclude>
+@IF OPENSSL
+ <ClInclude Include="..\dst_openssl.h">
+ <Filter>Dst Header Files</Filter>
+ </ClInclude>
+@END OPENSSL
+ <ClInclude Include="..\dst_parse.h">
+ <Filter>Dst Header Files</Filter>
+ </ClInclude>
+@IF PKCS11
+ <ClInclude Include="..\dst_pkcs11.h">
+ <Filter>Dst Header Files</Filter>
+ </ClInclude>
+@END PKCS11
+ <ClInclude Include="..\spnego.h">
+ <Filter>Dst Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+</Project>
diff --git a/lib/dns/win32/libdns.vcxproj.in b/lib/dns/win32/libdns.vcxproj.in
new file mode 100644
index 0000000..7ceb68b
--- /dev/null
+++ b/lib/dns/win32/libdns.vcxproj.in
@@ -0,0 +1,346 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|@PLATFORM@">
+ <Configuration>Debug</Configuration>
+ <Platform>@PLATFORM@</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|@PLATFORM@">
+ <Configuration>Release</Configuration>
+ <Platform>@PLATFORM@</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{5FEBFD4E-CCB0-48B9-B733-E15EEB85C16A}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>libdns</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>..\..\..\Build\$(Configuration)\</OutDir>
+ <IntDir>.\$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>..\..\..\Build\$(Configuration)\</OutDir>
+ <IntDir>.\$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>BIND9;WIN32;USE_MD5;@CRYPTO@@USE_GSSAPI@@USE_ISC_SPNEGO@_DEBUG;_WINDOWS;_USRDLL;LIBDNS_EXPORTS;%(PreprocessorDefinitions);%(PreprocessorDefinitions);%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>.\;..\..\..\;include;..\include;..\..\isc;..\..\isc\win32;..\..\isc\win32\include;..\..\isc\include;@LIBXML2_INC@@OPENSSL_INC@@GSSAPI_INC@@GEOIP_INC@%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <PrecompiledHeaderOutputFile>.\$(Configuration)\$(TargetName).pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>.\$(Configuration)\</AssemblerListingLocation>
+ <ObjectFileName>.\$(Configuration)\</ObjectFileName>
+ <ProgramDataBaseFileName>$(OutDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <BrowseInformation>true</BrowseInformation>
+ <CompileAs>CompileAsC</CompileAs>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile>
+ <AdditionalLibraryDirectories>..\..\isc\win32\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>libisc.lib;@LIBXML2_LIB@@OPENSSL_LIB@@GSSAPI_LIB@@KRB5_LIB@@GEOIP_LIB@ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <ModuleDefinitionFile>$(ProjectName).def</ModuleDefinitionFile>
+ <ImportLibrary>.\$(Configuration)\$(ProjectName).lib</ImportLibrary>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>@INTRINSIC@</IntrinsicFunctions>
+ <PreprocessorDefinitions>BIND9;WIN32;USE_MD5;@CRYPTO@@USE_GSSAPI@@USE_ISC_SPNEGO@NDEBUG;_WINDOWS;_USRDLL;LIBDNS_EXPORTS;%(PreprocessorDefinitions);%(PreprocessorDefinitions);%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>.\;..\..\..\;include;..\include;..\..\isc;..\..\isc\win32;..\..\isc\win32\include;..\..\isc\include;@LIBXML2_INC@@OPENSSL_INC@@GSSAPI_INC@@GEOIP_INC@%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <StringPooling>true</StringPooling>
+ <PrecompiledHeaderOutputFile>.\$(Configuration)\$(TargetName).pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>.\$(Configuration)\</AssemblerListingLocation>
+ <ObjectFileName>.\$(Configuration)\</ObjectFileName>
+ <ProgramDataBaseFileName>$(OutDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ <CompileAs>CompileAsC</CompileAs>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile>
+ <AdditionalLibraryDirectories>..\..\isc\win32\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>libisc.lib;@LIBXML2_LIB@@OPENSSL_LIB@@GSSAPI_LIB@@KRB5_LIB@@GEOIP_LIB@ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <ModuleDefinitionFile>$(ProjectName).def</ModuleDefinitionFile>
+ <ImportLibrary>.\$(Configuration)\$(ProjectName).lib</ImportLibrary>
+ <LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <None Include="libdns.def" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\acache.c" />
+ <ClCompile Include="..\acl.c" />
+ <ClCompile Include="..\adb.c" />
+ <ClCompile Include="..\badcache.c" />
+ <ClCompile Include="..\byaddr.c" />
+ <ClCompile Include="..\cache.c" />
+ <ClCompile Include="..\callbacks.c" />
+ <ClCompile Include="..\catz.c" />
+ <ClCompile Include="..\client.c" />
+ <ClCompile Include="..\clientinfo.c" />
+ <ClCompile Include="..\compress.c" />
+ <ClCompile Include="..\db.c" />
+ <ClCompile Include="..\dbiterator.c" />
+ <ClCompile Include="..\dbtable.c" />
+ <ClCompile Include="..\diff.c" />
+ <ClCompile Include="..\dispatch.c" />
+ <ClCompile Include="..\dlz.c" />
+ <ClCompile Include="..\dns64.c" />
+ <ClCompile Include="..\dnssec.c" />
+ <ClCompile Include="..\ds.c" />
+ <ClCompile Include="..\dst_api.c" />
+ <ClCompile Include="..\dst_lib.c" />
+ <ClCompile Include="..\dst_parse.c" />
+ <ClCompile Include="..\dst_result.c" />
+ <ClCompile Include="..\dyndb.c" />
+ <ClCompile Include="..\ecdb.c" />
+ <ClCompile Include="..\fixedname.c" />
+ <ClCompile Include="..\forward.c" />
+@IF GEOIP
+ <ClCompile Include="..\geoip.c" />
+@END GEOIP
+ <ClCompile Include="..\gssapictx.c" />
+ <ClCompile Include="..\gssapi_link.c" />
+ <ClCompile Include="..\hmac_link.c" />
+ <ClCompile Include="..\ipkeylist.c" />
+ <ClCompile Include="..\iptable.c" />
+ <ClCompile Include="..\journal.c" />
+ <ClCompile Include="..\key.c" />
+ <ClCompile Include="..\keydata.c" />
+ <ClCompile Include="..\keytable.c" />
+ <ClCompile Include="..\lib.c" />
+ <ClCompile Include="..\log.c" />
+ <ClCompile Include="..\lookup.c" />
+ <ClCompile Include="..\master.c" />
+ <ClCompile Include="..\masterdump.c" />
+ <ClCompile Include="..\message.c" />
+ <ClCompile Include="..\name.c" />
+ <ClCompile Include="..\ncache.c" />
+ <ClCompile Include="..\nsec.c" />
+ <ClCompile Include="..\nsec3.c" />
+ <ClCompile Include="..\nta.c" />
+@IF OPENSSL
+ <ClCompile Include="..\openssldh_link.c" />
+ <ClCompile Include="..\openssldsa_link.c" />
+ <ClCompile Include="..\opensslecdsa_link.c" />
+ <ClCompile Include="..\openssleddsa_link.c" />
+ <ClCompile Include="..\opensslgost_link.c" />
+ <ClCompile Include="..\opensslrsa_link.c" />
+ <ClCompile Include="..\openssl_link.c" />
+@END OPENSSL
+ <ClCompile Include="..\order.c" />
+ <ClCompile Include="..\peer.c" />
+@IF PKCS11
+ <ClCompile Include="..\pkcs11.c" />
+ <ClCompile Include="..\pkcs11dh_link.c" />
+ <ClCompile Include="..\pkcs11dsa_link.c" />
+ <ClCompile Include="..\pkcs11ecdsa_link.c" />
+ <ClCompile Include="..\pkcs11eddsa_link.c" />
+ <ClCompile Include="..\pkcs11gost_link.c" />
+ <ClCompile Include="..\pkcs11rsa_link.c" />
+@END PKCS11
+ <ClCompile Include="..\portlist.c" />
+ <ClCompile Include="..\private.c" />
+ <ClCompile Include="..\rbt.c" />
+ <ClCompile Include="..\rbtdb.c" />
+ <ClCompile Include="..\rbtdb64.c" />
+ <ClCompile Include="..\rcode.c" />
+ <ClCompile Include="..\rdata.c" />
+ <ClCompile Include="..\rdatalist.c" />
+ <ClCompile Include="..\rdataset.c" />
+ <ClCompile Include="..\rdatasetiter.c" />
+ <ClCompile Include="..\rdataslab.c" />
+ <ClCompile Include="..\request.c" />
+ <ClCompile Include="..\resolver.c" />
+ <ClCompile Include="..\result.c" />
+ <ClCompile Include="..\rootns.c" />
+ <ClCompile Include="..\rpz.c" />
+ <ClCompile Include="..\rriterator.c" />
+ <ClCompile Include="..\rrl.c" />
+ <ClCompile Include="..\sdb.c" />
+ <ClCompile Include="..\sdlz.c" />
+ <ClCompile Include="..\soa.c" />
+ <ClCompile Include="..\spnego.c" />
+ <ClCompile Include="..\ssu.c" />
+ <ClCompile Include="..\ssu_external.c" />
+ <ClCompile Include="..\stats.c" />
+ <ClCompile Include="..\tcpmsg.c" />
+ <ClCompile Include="..\time.c" />
+ <ClCompile Include="..\timer.c" />
+ <ClCompile Include="..\tkey.c" />
+ <ClCompile Include="..\tsec.c" />
+ <ClCompile Include="..\tsig.c" />
+ <ClCompile Include="..\ttl.c" />
+ <ClCompile Include="..\update.c" />
+ <ClCompile Include="..\validator.c" />
+ <ClCompile Include="..\view.c" />
+ <ClCompile Include="..\xfrin.c" />
+ <ClCompile Include="..\zone.c" />
+ <ClCompile Include="..\zonekey.c" />
+ <ClCompile Include="..\zt.c" />
+ <ClCompile Include="DLLMain.c" />
+ <ClCompile Include="version.c" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\code.h" />
+ <ClInclude Include="..\dst_gost.h" />
+ <ClInclude Include="..\dst_internal.h" />
+@IF OPENSSL
+ <ClInclude Include="..\dst_openssl.h" />
+@END OPENSSL
+ <ClInclude Include="..\dst_parse.h" />
+@IF PKCS11
+ <ClInclude Include="..\dst_pkcs11.h" />
+@END PKCS11
+ <ClInclude Include="..\include\dns\acache.h" />
+ <ClInclude Include="..\include\dns\acl.h" />
+ <ClInclude Include="..\include\dns\adb.h" />
+ <ClInclude Include="..\include\dns\badcache.h" />
+ <ClInclude Include="..\include\dns\bit.h" />
+ <ClInclude Include="..\include\dns\byaddr.h" />
+ <ClInclude Include="..\include\dns\cache.h" />
+ <ClInclude Include="..\include\dns\callbacks.h" />
+ <ClInclude Include="..\include\dns\catz.h" />
+ <ClInclude Include="..\include\dns\cert.h" />
+ <ClInclude Include="..\include\dns\client.h" />
+ <ClInclude Include="..\include\dns\clientinfo.h" />
+ <ClInclude Include="..\include\dns\compress.h" />
+ <ClInclude Include="..\include\dns\db.h" />
+ <ClInclude Include="..\include\dns\dbiterator.h" />
+ <ClInclude Include="..\include\dns\dbtable.h" />
+ <ClInclude Include="..\include\dns\diff.h" />
+ <ClInclude Include="..\include\dns\dispatch.h" />
+ <ClInclude Include="..\include\dns\dlz.h" />
+ <ClInclude Include="..\include\dns\dns64.h" />
+ <ClInclude Include="..\include\dns\dnssec.h" />
+ <ClInclude Include="..\include\dns\dnstap.h" />
+ <ClInclude Include="..\include\dns\ds.h" />
+ <ClInclude Include="..\include\dns\dsdigest.h" />
+ <ClInclude Include="..\include\dns\dyndb.h" />
+ <ClInclude Include="..\include\dns\ecdb.h" />
+ <ClInclude Include="..\include\dns\edns.h" />
+ <ClInclude Include="..\include\dns\enumclass.h" />
+ <ClInclude Include="..\include\dns\enumtype.h" />
+ <ClInclude Include="..\include\dns\events.h" />
+ <ClInclude Include="..\include\dns\fixedname.h" />
+ <ClInclude Include="..\include\dns\forward.h" />
+@IF GEOIP
+ <ClInclude Include="..\include\dns\geoip.h" />
+@END GEOIP
+ <ClInclude Include="..\include\dns\ipkeylist.h" />
+ <ClInclude Include="..\include\dns\iptable.h" />
+ <ClInclude Include="..\include\dns\journal.h" />
+ <ClInclude Include="..\include\dns\keydata.h" />
+ <ClInclude Include="..\include\dns\keyflags.h" />
+ <ClInclude Include="..\include\dns\keytable.h" />
+ <ClInclude Include="..\include\dns\keyvalues.h" />
+ <ClInclude Include="..\include\dns\lib.h" />
+ <ClInclude Include="..\include\dns\log.h" />
+ <ClInclude Include="..\include\dns\lookup.h" />
+ <ClInclude Include="..\include\dns\master.h" />
+ <ClInclude Include="..\include\dns\masterdump.h" />
+ <ClInclude Include="..\include\dns\message.h" />
+ <ClInclude Include="..\include\dns\name.h" />
+ <ClInclude Include="..\include\dns\ncache.h" />
+ <ClInclude Include="..\include\dns\nsec.h" />
+ <ClInclude Include="..\include\dns\nsec3.h" />
+ <ClInclude Include="..\include\dns\nta.h" />
+ <ClInclude Include="..\include\dns\opcode.h" />
+ <ClInclude Include="..\include\dns\order.h" />
+ <ClInclude Include="..\include\dns\peer.h" />
+ <ClInclude Include="..\include\dns\portlist.h" />
+ <ClInclude Include="..\include\dns\private.h" />
+ <ClInclude Include="..\include\dns\rbt.h" />
+ <ClInclude Include="..\include\dns\rcode.h" />
+ <ClInclude Include="..\include\dns\rdata.h" />
+ <ClInclude Include="..\include\dns\rdataclass.h" />
+ <ClInclude Include="..\include\dns\rdatalist.h" />
+ <ClInclude Include="..\include\dns\rdataset.h" />
+ <ClInclude Include="..\include\dns\rdatasetiter.h" />
+ <ClInclude Include="..\include\dns\rdataslab.h" />
+ <ClInclude Include="..\include\dns\rdatastruct.h" />
+ <ClInclude Include="..\include\dns\rdatatype.h" />
+ <ClInclude Include="..\include\dns\request.h" />
+ <ClInclude Include="..\include\dns\resolver.h" />
+ <ClInclude Include="..\include\dns\result.h" />
+ <ClInclude Include="..\include\dns\rootns.h" />
+ <ClInclude Include="..\include\dns\rpz.h" />
+ <ClInclude Include="..\include\dns\rriterator.h" />
+ <ClInclude Include="..\include\dns\rrl.h" />
+ <ClInclude Include="..\include\dns\sdb.h" />
+ <ClInclude Include="..\include\dns\sdlz.h" />
+ <ClInclude Include="..\include\dns\secalg.h" />
+ <ClInclude Include="..\include\dns\secproto.h" />
+ <ClInclude Include="..\include\dns\soa.h" />
+ <ClInclude Include="..\include\dns\ssu.h" />
+ <ClInclude Include="..\include\dns\stats.h" />
+ <ClInclude Include="..\include\dns\tcpmsg.h" />
+ <ClInclude Include="..\include\dns\time.h" />
+ <ClInclude Include="..\include\dns\timer.h" />
+ <ClInclude Include="..\include\dns\tkey.h" />
+ <ClInclude Include="..\include\dns\tsec.h" />
+ <ClInclude Include="..\include\dns\tsig.h" />
+ <ClInclude Include="..\include\dns\ttl.h" />
+ <ClInclude Include="..\include\dns\types.h" />
+ <ClInclude Include="..\include\dns\update.h" />
+ <ClInclude Include="..\include\dns\validator.h" />
+ <ClInclude Include="..\include\dns\version.h" />
+ <ClInclude Include="..\include\dns\view.h" />
+ <ClInclude Include="..\include\dns\xfrin.h" />
+ <ClInclude Include="..\include\dns\zone.h" />
+ <ClInclude Include="..\include\dns\zonekey.h" />
+ <ClInclude Include="..\include\dns\zt.h" />
+ <ClInclude Include="..\include\dst\dst.h" />
+ <ClInclude Include="..\include\dst\gssapi.h" />
+ <ClInclude Include="..\include\dst\lib.h" />
+ <ClInclude Include="..\include\dst\result.h" />
+ <ClInclude Include="..\rbtdb.h" />
+ <ClInclude Include="..\rbtdb64.h" />
+ <ClInclude Include="..\rdatalist_p.h" />
+ <ClInclude Include="..\spnego.h" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/lib/dns/win32/libdns.vcxproj.user b/lib/dns/win32/libdns.vcxproj.user
new file mode 100644
index 0000000..695b5c7
--- /dev/null
+++ b/lib/dns/win32/libdns.vcxproj.user
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+</Project> \ No newline at end of file
diff --git a/lib/dns/win32/version.c b/lib/dns/win32/version.c
new file mode 100644
index 0000000..852f70e
--- /dev/null
+++ b/lib/dns/win32/version.c
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#include <versions.h>
+
+#include <dns/version.h>
+
+LIBDNS_EXTERNAL_DATA const char dns_version[] = VERSION;
+LIBDNS_EXTERNAL_DATA const char dns_major[] = MAJOR;
+LIBDNS_EXTERNAL_DATA const char dns_mapapi[] = MAPAPI;
+
+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..d39ca26
--- /dev/null
+++ b/lib/dns/xfrin.c
@@ -0,0 +1,1618 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.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/callbacks.h>
+#include <dns/catz.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_IXFR_END,
+ XFRST_AXFR,
+ XFRST_AXFR_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 */
+ bool shuttingdown;
+ isc_result_t shutdown_result;
+
+ dns_name_t name; /*%< Name of zone to transfer */
+ dns_rdataclass_t rdclass;
+
+ bool 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_dscp_t dscp;
+
+ 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;
+ bool 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;
+ uint32_t end_serial;
+ bool is_ixfr;
+
+ unsigned int nmsg; /*%< Number of messages recvd */
+ unsigned int nrecs; /*%< Number of records recvd */
+ uint64_t nbytes; /*%< Number of bytes received */
+
+ unsigned int maxrecords; /*%< The maximum number of
+ records set for the zone */
+
+ 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.
+ */
+ dns_rdatacallbacks_t axfr;
+
+ struct {
+ uint32_t request_serial;
+ 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,
+ isc_dscp_t dscp,
+ 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 axfr_finalize(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,
+ 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_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 = false;
+
+ if (xfr->db != NULL)
+ dns_db_detach(&xfr->db);
+
+ CHECK(axfr_makedb(xfr, &xfr->db));
+ dns_rdatacallbacks_init(&xfr->axfr);
+ CHECK(dns_db_beginload(xfr->db, &xfr->axfr));
+ result = ISC_R_SUCCESS;
+ failure:
+ return (result);
+}
+
+static isc_result_t
+axfr_makedb(dns_xfrin_ctx_t *xfr, dns_db_t **dbp) {
+ isc_result_t result;
+
+ result = dns_db_create(xfr->mctx, /* XXX */
+ "rbt", /* XXX guess */
+ &xfr->name,
+ dns_dbtype_zone,
+ xfr->rdclass,
+ 0, NULL, /* XXX guess */
+ dbp);
+ if (result == ISC_R_SUCCESS) {
+ dns_zone_rpz_enable_db(xfr->zone, *dbp);
+ dns_zone_catz_enable_db(xfr->zone, *dbp);
+ }
+ return (result);
+}
+
+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;
+
+ if (rdata->rdclass != xfr->rdclass)
+ return(DNS_R_BADCLASS);
+
+ 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;
+ uint64_t records;
+
+ CHECK(dns_diff_load(&xfr->diff, xfr->axfr.add, xfr->axfr.add_private));
+ xfr->difflen = 0;
+ dns_diff_clear(&xfr->diff);
+ if (xfr->maxrecords != 0U) {
+ result = dns_db_getsize(xfr->db, xfr->ver, &records, NULL);
+ if (result == ISC_R_SUCCESS && records > xfr->maxrecords) {
+ result = DNS_R_TOOMANYRECORDS;
+ goto failure;
+ }
+ }
+ 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));
+
+ result = ISC_R_SUCCESS;
+ failure:
+ return (result);
+}
+
+static isc_result_t
+axfr_finalize(dns_xfrin_ctx_t *xfr) {
+ isc_result_t result;
+
+ CHECK(dns_zone_replacedb(xfr->zone, xfr->db, 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 = true;
+ INSIST(xfr->db != NULL);
+ xfr->difflen = 0;
+
+ journalfile = dns_zone_getjournal(xfr->zone);
+ if (journalfile != NULL)
+ CHECK(dns_journal_open(xfr->mctx, journalfile,
+ DNS_JOURNAL_CREATE, &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 (rdata->rdclass != xfr->rdclass)
+ return(DNS_R_BADCLASS);
+
+ 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;
+ uint64_t records;
+
+ 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->maxrecords != 0U) {
+ result = dns_db_getsize(xfr->db, xfr->ver, &records, NULL);
+ if (result == ISC_R_SUCCESS && records > xfr->maxrecords) {
+ result = DNS_R_TOOMANYRECORDS;
+ goto failure;
+ }
+ }
+ 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, 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, 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 = 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) {
+ 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) {
+ uint32_t soa_serial = dns_soa_getserial(rdata);
+ if (soa_serial == xfr->end_serial) {
+ CHECK(ixfr_commit(xfr));
+ xfr->state = XFRST_IXFR_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_AXFR_END;
+ break;
+ }
+ break;
+ case XFRST_AXFR_END:
+ case XFRST_IXFR_END:
+ FAIL(DNS_R_EXTRADATA);
+ /* NOTREACHED */
+ /* FALLTHROUGH */
+ 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;
+ isc_dscp_t dscp;
+
+ switch (isc_sockaddr_pf(masteraddr)) {
+ case PF_INET:
+ sourceaddr = *dns_zone_getxfrsource4(zone);
+ dscp = dns_zone_getxfrsource4dscp(zone);
+ break;
+ case PF_INET6:
+ sourceaddr = *dns_zone_getxfrsource6(zone);
+ dscp = dns_zone_getxfrsource6dscp(zone);
+ break;
+ default:
+ INSIST(0);
+ }
+
+ return(dns_xfrin_create3(zone, xfrtype, masteraddr, &sourceaddr,
+ dscp, 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)
+{
+ return (dns_xfrin_create3(zone, xfrtype, masteraddr, sourceaddr, -1,
+ tsigkey, mctx, timermgr, socketmgr, task,
+ done, xfrp));
+}
+
+isc_result_t
+dns_xfrin_create3(dns_zone_t *zone, dns_rdatatype_t xfrtype,
+ isc_sockaddr_t *masteraddr, isc_sockaddr_t *sourceaddr,
+ isc_dscp_t dscp, 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, dscp, tsigkey, &xfr));
+
+ CHECK(xfrin_start(xfr));
+
+ xfr->done = done;
+ if (xfr->done != NULL)
+ 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);
+
+ if (xfr->tcpmsg_valid) {
+ dns_tcpmsg_invalidate(&xfr->tcpmsg);
+ xfr->tcpmsg_valid = false;
+ }
+
+ if (xfr->ver != NULL)
+ dns_db_closeversion(xfr->db, &xfr->ver, false);
+}
+
+
+static void
+xfrin_fail(dns_xfrin_ctx_t *xfr, isc_result_t result, const char *msg) {
+ if (result != DNS_R_UPTODATE && result != DNS_R_TOOMANYRECORDS) {
+ 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 = true;
+ xfr->shutdown_result = result;
+ 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,
+ isc_dscp_t dscp,
+ dns_tsigkey_t *tsigkey,
+ dns_xfrin_ctx_t **xfrp)
+{
+ dns_xfrin_ctx_t *xfr = NULL;
+ isc_result_t result;
+ uint32_t tmp;
+
+ xfr = isc_mem_get(mctx, sizeof(*xfr));
+ if (xfr == NULL)
+ return (ISC_R_NOMEMORY);
+ xfr->mctx = NULL;
+ isc_mem_attach(mctx, &xfr->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 = false;
+ xfr->shutdown_result = ISC_R_UNSET;
+
+ dns_name_init(&xfr->name, NULL);
+ xfr->rdclass = rdclass;
+ isc_random_get(&tmp);
+ xfr->checkid = true;
+ xfr->id = (uint16_t)(tmp & 0xffff);
+ xfr->reqtype = reqtype;
+ xfr->dscp = dscp;
+
+ /* sockaddr */
+ xfr->socket = NULL;
+ /* qbuffer */
+ /* qbuffer_data */
+ /* tcpmsg */
+ xfr->tcpmsg_valid = 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;
+ xfr->maxrecords = dns_zone_getmaxrecords(zone);
+ 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 = false;
+
+ /* ixfr.request_serial */
+ /* ixfr.current_serial */
+ xfr->ixfr.journal = NULL;
+
+ xfr->axfr.add = 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),
+ false));
+
+ xfr->masteraddr = *masteraddr;
+
+ INSIST(isc_sockaddr_pf(masteraddr) == isc_sockaddr_pf(sourceaddr));
+ xfr->sourceaddr = *sourceaddr;
+ isc_sockaddr_setport(&xfr->sourceaddr, 0);
+
+ /*
+ * Reserve 2 bytes for TCP length at the begining of the buffer.
+ */
+ isc_buffer_init(&xfr->qbuffer, &xfr->qbuffer_data[2],
+ sizeof(xfr->qbuffer_data) - 2);
+
+ 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_putanddetach(&xfr->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
+ isc_socket_dscp(xfr->socket, xfr->dscp);
+ 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;
+ bool cleanup_cctx = false;
+ isc_result_t result;
+
+ CHECK(dns_compress_init(&cctx, -1, mctx));
+ cleanup_cctx = 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];
+ char signerbuf[DNS_NAME_FORMATSIZE];
+ const char *signer = "", *sep = "";
+ isc_sockaddr_t sockaddr;
+ dns_zonemgr_t * zmgr;
+ isc_time_t now;
+
+ 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;
+ }
+
+ zmgr = dns_zone_getmgr(xfr->zone);
+ if (zmgr != NULL) {
+ if (result != ISC_R_SUCCESS) {
+ TIME_NOW(&now);
+ dns_zonemgr_unreachableadd(zmgr, &xfr->masteraddr,
+ &xfr->sourceaddr, &now);
+ goto failure;
+ } else
+ dns_zonemgr_unreachabledel(zmgr, &xfr->masteraddr,
+ &xfr->sourceaddr);
+ }
+
+ result = isc_socket_getsockname(xfr->socket, &sockaddr);
+ if (result == ISC_R_SUCCESS) {
+ isc_sockaddr_format(&sockaddr, sourcetext, sizeof(sourcetext));
+ } else {
+ strlcpy(sourcetext, "<UNKNOWN>", sizeof(sourcetext));
+ }
+
+ if (xfr->tsigkey != NULL && xfr->tsigkey->key != NULL) {
+ dns_name_format(dst_key_name(xfr->tsigkey->key),
+ signerbuf, sizeof(signerbuf));
+ sep = " TSIG ";
+ signer = signerbuf;
+ }
+
+ xfrin_log(xfr, ISC_LOG_INFO, "connected using %s%s%s",
+ sourcetext, sep, signer);
+
+ dns_tcpmsg_init(xfr->mctx, xfr->socket, &xfr->tcpmsg);
+ xfr->tcpmsg_valid = 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));
+ 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;
+ dns_rdataset_t *qrdataset = NULL;
+ dns_message_t *msg = NULL;
+ 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_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 = 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);
+
+ /*
+ * Record message length and adjust region to include TCP
+ * length field.
+ */
+ xfr->qbuffer_data[0] = (region.length >> 8) & 0xff;
+ xfr->qbuffer_data[1] = region.length & 0xff;
+ region.base -= 2;
+ region.length += 2;
+ CHECK(isc_socket_send(xfr->socket, &region, xfr->task,
+ xfrin_send_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, false);
+ return (result);
+}
+
+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;
+
+ dns_message_setclass(msg, xfr->rdclass);
+
+ if (xfr->nmsg > 0)
+ msg->tcp_continuation = 1;
+
+ result = dns_message_parse(msg, &tcpmsg->buffer,
+ DNS_MESSAGEPARSE_PRESERVEORDER);
+
+ if (result == ISC_R_SUCCESS)
+ dns_message_logpacket2(msg, "received message from",
+ &tcpmsg->address,
+ DNS_LOGCATEGORY_XFER_IN,
+ DNS_LOGMODULE_XFER_IN,
+ ISC_LOG_DEBUG(10), xfr->mctx);
+ else
+ xfrin_log(xfr, ISC_LOG_DEBUG(10), "dns_message_parse: %s",
+ dns_result_totext(result));
+
+ if (result != ISC_R_SUCCESS || msg->rcode != dns_rcode_noerror ||
+ msg->opcode != dns_opcode_query ||msg->rdclass != xfr->rdclass ||
+ (xfr->checkid && msg->id != xfr->id)) {
+ if (result == ISC_R_SUCCESS && msg->rcode != dns_rcode_noerror)
+ result = ISC_RESULTCLASS_DNSRCODE + msg->rcode; /*XXX*/
+ else if (result == ISC_R_SUCCESS &&
+ msg->opcode != dns_opcode_query)
+ result = DNS_R_UNEXPECTEDOPCODE;
+ else if (result == ISC_R_SUCCESS &&
+ msg->rdclass != xfr->rdclass)
+ result = DNS_R_BADCLASS;
+ else if (result == ISC_R_SUCCESS || result == DNS_R_NOERROR)
+ result = DNS_R_UNEXPECTEDID;
+ if (xfr->reqtype == dns_rdatatype_axfr ||
+ xfr->reqtype == dns_rdatatype_soa)
+ goto failure;
+ 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));
+ goto failure;
+ }
+
+ 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_AXFR_END ||
+ xfr->state == XFRST_IXFR_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);
+
+ switch (xfr->state) {
+ case XFRST_GOTSOA:
+ xfr->reqtype = dns_rdatatype_axfr;
+ xfr->state = XFRST_INITIALSOA;
+ CHECK(xfrin_send_request(xfr));
+ break;
+ case XFRST_AXFR_END:
+ CHECK(axfr_finalize(xfr));
+ /* FALLTHROUGH */
+ case XFRST_IXFR_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 = true;
+ xfr->shutdown_result = ISC_R_SUCCESS;
+ maybe_free(xfr);
+ break;
+ default:
+ /*
+ * 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) {
+ uint64_t msecs;
+ uint64_t persec;
+ const char *result_str;
+
+ REQUIRE(VALID_XFRIN(xfr));
+
+ if (! xfr->shuttingdown || xfr->refcount != 0 ||
+ xfr->connects != 0 || xfr->sends != 0 ||
+ xfr->recvs != 0)
+ return;
+
+ INSIST(! xfr->shuttingdown || xfr->shutdown_result != ISC_R_UNSET);
+
+ /* If we're called through dns_xfrin_detach() and are not
+ * shutting down, we can't know what the transfer status is as
+ * we are only called when the last reference is lost.
+ */
+ result_str = (xfr->shuttingdown ?
+ isc_result_totext(xfr->shutdown_result) : "unknown");
+ xfrin_log(xfr, ISC_LOG_INFO, "Transfer status: %s", result_str);
+
+ /*
+ * 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, "
+ "%" PRIu64 " 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);
+
+ 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, false);
+
+ if (xfr->db != NULL)
+ dns_db_detach(&xfr->db);
+
+ if (xfr->zone != NULL)
+ dns_zone_idetach(&xfr->zone);
+
+ isc_mem_putanddetach(&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) == 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) == 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..6d4426a
--- /dev/null
+++ b/lib/dns/zone.c
@@ -0,0 +1,19485 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+#include <errno.h>
+#include <stdbool.h>
+
+#include <isc/file.h>
+#include <isc/hex.h>
+#include <isc/mutex.h>
+#include <isc/pool.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/stats.h>
+#include <isc/stdtime.h>
+#include <isc/strerror.h>
+#include <isc/string.h>
+#include <isc/taskpool.h>
+#include <isc/thread.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/catz.h>
+#include <dns/db.h>
+#include <dns/dbiterator.h>
+#include <dns/dlz.h>
+#include <dns/dnssec.h>
+#include <dns/events.h>
+#include <dns/journal.h>
+#include <dns/keydata.h>
+#include <dns/keytable.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/private.h>
+#include <dns/rcode.h>
+#include <dns/rdata.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/rriterator.h>
+#include <dns/soa.h>
+#include <dns/ssu.h>
+#include <dns/stats.h>
+#include <dns/time.h>
+#include <dns/tsig.h>
+#include <dns/update.h>
+#include <dns/xfrin.h>
+#include <dns/zone.h>
+#include <dns/zt.h>
+
+#include <dst/dst.h>
+
+#include "zone_p.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)
+
+/*%
+ * Key flags
+ */
+#define REVOKE(x) ((dst_key_flags(x) & DNS_KEYFLAG_REVOKE) != 0)
+#define KSK(x) ((dst_key_flags(x) & DNS_KEYFLAG_KSK) != 0)
+#define ALG(x) dst_key_alg(x)
+
+/*
+ * 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 */
+#define RESIGN_DELAY 3600 /*%< 1 hour */
+
+#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 ISC_LIST(dns_forward_t) dns_forwardlist_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;
+typedef struct dns_keyfetch dns_keyfetch_t;
+typedef struct dns_asyncload dns_asyncload_t;
+typedef struct dns_include dns_include_t;
+
+#define DNS_ZONE_CHECKLOCK
+#ifdef DNS_ZONE_CHECKLOCK
+#define LOCK_ZONE(z) \
+ do { LOCK(&(z)->lock); \
+ INSIST((z)->locked == false); \
+ (z)->locked = true; \
+ } while (0)
+#define UNLOCK_ZONE(z) \
+ do { (z)->locked = false; UNLOCK(&(z)->lock); } while (0)
+#define LOCKED_ZONE(z) ((z)->locked)
+#define TRYLOCK_ZONE(result, z) \
+ do { \
+ result = isc_mutex_trylock(&(z)->lock); \
+ if (result == ISC_R_SUCCESS) { \
+ INSIST((z)->locked == false); \
+ (z)->locked = true; \
+ } \
+ } while (0)
+#else
+#define LOCK_ZONE(z) LOCK(&(z)->lock)
+#define UNLOCK_ZONE(z) UNLOCK(&(z)->lock)
+#define LOCKED_ZONE(z) true
+#define TRYLOCK_ZONE(result, z) \
+ do { result = isc_mutex_trylock(&(z)->lock); } while (0)
+#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
+
+#ifdef ENABLE_AFL
+extern bool dns_fuzzing_resolver;
+#endif
+
+struct dns_zone {
+ /* Unlocked */
+ unsigned int magic;
+ isc_mutex_t lock;
+#ifdef DNS_ZONE_CHECKLOCK
+ bool 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;
+ ISC_LIST(dns_include_t) includes; /* Include files */
+ ISC_LIST(dns_include_t) newincludes; /* Loading */
+ unsigned int nincludes;
+ dns_masterformat_t masterformat;
+ const dns_master_style_t *masterstyle;
+ char *journal;
+ int32_t journalsize;
+ dns_rdataclass_t rdclass;
+ dns_zonetype_t type;
+ unsigned int flags;
+ unsigned int options;
+ unsigned int options2;
+ 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_time_t refreshkeytime;
+ uint32_t refreshkeyinterval;
+ uint32_t refreshkeycount;
+ uint32_t refresh;
+ uint32_t retry;
+ uint32_t expire;
+ uint32_t minimum;
+ isc_stdtime_t key_expiry;
+ isc_stdtime_t log_key_expired_timer;
+ char *keydirectory;
+
+ uint32_t maxrefresh;
+ uint32_t minrefresh;
+ uint32_t maxretry;
+ uint32_t minretry;
+
+ uint32_t maxrecords;
+
+ isc_sockaddr_t *masters;
+ isc_dscp_t *masterdscps;
+ dns_name_t **masterkeynames;
+ bool *mastersok;
+ unsigned int masterscnt;
+ unsigned int curmaster;
+ isc_sockaddr_t masteraddr;
+ dns_notifytype_t notifytype;
+ isc_sockaddr_t *notify;
+ dns_name_t **notifykeynames;
+ isc_dscp_t *notifydscp;
+ unsigned int notifycnt;
+ isc_sockaddr_t notifyfrom;
+ isc_task_t *task;
+ isc_task_t *loadtask;
+ 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;
+ isc_dscp_t notifysrc4dscp;
+ isc_dscp_t notifysrc6dscp;
+ isc_dscp_t xfrsource4dscp;
+ isc_dscp_t xfrsource6dscp;
+ isc_dscp_t altxfrsource4dscp;
+ isc_dscp_t altxfrsource6dscp;
+ 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;
+ bool update_disabled;
+ bool 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;
+ uint32_t maxxfrin;
+ uint32_t maxxfrout;
+ uint32_t idlein;
+ uint32_t idleout;
+ isc_event_t ctlevent;
+ dns_ssutable_t *ssutable;
+ uint32_t sigvalidityinterval;
+ uint32_t sigresigninginterval;
+ dns_view_t *view;
+ dns_view_t *prev_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.
+ */
+ isc_stats_t *stats;
+ /*%
+ * Optional per-zone statistics counters. Counted outside of this
+ * module.
+ */
+ dns_zonestat_level_t statlevel;
+ bool requeststats_on;
+ isc_stats_t *requeststats;
+ dns_stats_t *rcvquerystats;
+ uint32_t notifydelay;
+ dns_isselffunc_t isself;
+ void *isselfarg;
+
+ char * strnamerd;
+ char * strname;
+ char * strrdclass;
+ char * strviewname;
+
+ /*%
+ * Serial number for deferred journal compaction.
+ */
+ uint32_t compact_serial;
+ /*%
+ * Keys that are signing the zone for the first time.
+ */
+ dns_signinglist_t signing;
+ dns_nsec3chainlist_t nsec3chain;
+ /*%
+ * List of outstanding NSEC3PARAM change requests.
+ */
+ isc_eventlist_t setnsec3param_queue;
+ /*%
+ * Signing / re-signing quantum stopping parameters.
+ */
+ uint32_t signatures;
+ uint32_t nodes;
+ dns_rdatatype_t privatetype;
+
+ /*%
+ * Autosigning/key-maintenance options
+ */
+ uint32_t keyopts;
+
+ /*%
+ * True if added by "rndc addzone"
+ */
+ bool added;
+
+ /*%
+ * True if added by automatically by named.
+ */
+ bool automatic;
+
+ /*%
+ * response policy data to be relayed to the database
+ */
+ dns_rpz_zones_t *rpzs;
+ dns_rpz_num_t rpz_num;
+
+ /*%
+ * catalog zone data
+ */
+ dns_catz_zones_t *catzs;
+
+ /*%
+ * parent catalog zone
+ */
+ dns_catz_zone_t *parentcatz;
+
+ /*%
+ * Serial number update method.
+ */
+ dns_updatemethod_t updatemethod;
+
+ /*%
+ * whether ixfr is requested
+ */
+ bool requestixfr;
+
+ /*%
+ * whether EDNS EXPIRE is requested
+ */
+ bool requestexpire;
+
+ /*%
+ * Outstanding forwarded UPDATE requests.
+ */
+ dns_forwardlist_t forwards;
+
+ dns_zone_t *raw;
+ dns_zone_t *secure;
+
+ bool sourceserialset;
+ uint32_t sourceserial;
+
+ /*%
+ * maximum zone ttl
+ */
+ dns_ttl_t maxttl;
+
+ /*
+ * Inline zone signing state.
+ */
+ dns_diff_t rss_diff;
+ isc_eventlist_t rss_events;
+ dns_dbversion_t *rss_newver;
+ dns_dbversion_t *rss_oldver;
+ dns_db_t *rss_db;
+ dns_zone_t *rss_raw;
+ isc_event_t *rss_event;
+ dns_update_state_t *rss_state;
+};
+
+#define zonediff_init(z, d) \
+ do { \
+ dns__zonediff_t *_z = (z); \
+ (_z)->diff = (d); \
+ (_z)->offline = false; \
+ } while (0)
+
+#define DNS_ZONE_FLAG(z,f) ((z)->flags & (f))
+#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
+ * occurred */
+#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_ZONEFLG_REFRESHING 0x04000000U /*%< Refreshing keydata */
+#define DNS_ZONEFLG_THAW 0x08000000U
+#define DNS_ZONEFLG_LOADPENDING 0x10000000U /*%< Loading scheduled */
+#define DNS_ZONEFLG_NODELAY 0x20000000U
+#define DNS_ZONEFLG_SENDSECURE 0x40000000U
+#define DNS_ZONEFLG_NEEDSTARTUPNOTIFY 0x80000000U /*%< need to send out notify
+ * due to the zone just
+ * being loaded for the
+ * first time. */
+
+#define DNS_ZONE_OPTION(z,o) (((z)->options & (o)) != 0)
+#define DNS_ZONE_OPTION2(z,o) (((z)->options2 & (o)) != 0)
+#define DNS_ZONEKEY_OPTION(z,o) (((z)->keyopts & (o)) != 0)
+
+/* Flags for zone_load() */
+#define DNS_ZONELOADFLAG_NOSTAT 0x00000001U /* Do not stat() master files */
+#define DNS_ZONELOADFLAG_THAW 0x00000002U /* Thaw the zone on successful
+ load. */
+
+#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;
+ uint32_t expire;
+ uint32_t last;
+ uint32_t count;
+};
+
+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_taskpool_t * loadtasks;
+ isc_task_t * task;
+ isc_pool_t * mctxpool;
+ isc_ratelimiter_t * notifyrl;
+ isc_ratelimiter_t * refreshrl;
+ isc_ratelimiter_t * startupnotifyrl;
+ isc_ratelimiter_t * startuprefreshrl;
+ isc_rwlock_t rwlock;
+ isc_mutex_t iolock;
+ isc_rwlock_t urlock;
+
+ /* Locked by rwlock. */
+ dns_zonelist_t zones;
+ dns_zonelist_t waiting_for_xfrin;
+ dns_zonelist_t xfrin_in_progress;
+
+ /* Configuration data. */
+ uint32_t transfersin;
+ uint32_t transfersperns;
+ unsigned int notifyrate;
+ unsigned int startupnotifyrate;
+ unsigned int serialqueryrate;
+ unsigned int startupserialqueryrate;
+
+ /* Locked by iolock */
+ uint32_t iolimit;
+ uint32_t ioactive;
+ dns_iolist_t high;
+ dns_iolist_t low;
+
+ /* Locked by urlock. */
+ /* 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;
+ dns_tsigkey_t *key;
+ isc_dscp_t dscp;
+ ISC_LINK(dns_notify_t) link;
+ isc_event_t *event;
+};
+
+#define DNS_NOTIFY_NOSOA 0x0001U
+#define DNS_NOTIFY_STARTUP 0x0002U
+
+/*%
+ * 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;
+ uint32_t which;
+ isc_sockaddr_t addr;
+ dns_updatecallback_t callback;
+ void *callback_arg;
+ unsigned int options;
+ ISC_LINK(dns_forward_t) link;
+};
+
+/*%
+ * Hold IO request state.
+ */
+struct dns_io {
+ unsigned int magic;
+ dns_zonemgr_t *zmgr;
+ bool 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;
+ uint16_t keyid;
+ bool deleteit;
+ bool 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];
+ bool done;
+ bool seen_nsec;
+ bool delete_nsec;
+ bool 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.
+ */
+
+struct dns_keyfetch {
+ dns_fixedname_t name;
+ dns_rdataset_t keydataset;
+ dns_rdataset_t dnskeyset;
+ dns_rdataset_t dnskeysigset;
+ dns_zone_t *zone;
+ dns_db_t *db;
+ dns_fetch_t *fetch;
+};
+
+/*%
+ * Hold state for an asynchronous load
+ */
+struct dns_asyncload {
+ dns_zone_t *zone;
+ dns_zt_zoneloaded_t loaded;
+ void *loaded_arg;
+ bool newonly;
+};
+
+/*%
+ * Reference to an include file encountered during loading
+ */
+struct dns_include {
+ char *name;
+ isc_time_t filetime;
+ ISC_LINK(dns_include_t) link;
+};
+
+/*
+ * These can be overridden by the -T mkeytimers option on the command
+ * line, so that we can test with shorter periods than specified in
+ * RFC 5011.
+ */
+#define HOUR 3600
+#define DAY (24*HOUR)
+#define MONTH (30*DAY)
+LIBDNS_EXTERNAL_DATA unsigned int dns_zone_mkey_hour = HOUR;
+LIBDNS_EXTERNAL_DATA unsigned int dns_zone_mkey_day = DAY;
+LIBDNS_EXTERNAL_DATA unsigned int dns_zone_mkey_month = MONTH;
+
+#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 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);
+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,
+ bool 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);
+static isc_result_t zone_send_secureserial(dns_zone_t *zone,
+ uint32_t serial);
+
+#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 *, bool);
+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, bool multi);
+static void zonemgr_free(dns_zonemgr_t *zmgr);
+static isc_result_t zonemgr_getio(dns_zonemgr_t *zmgr, bool 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, uint32_t *serial,
+ uint32_t *refresh, uint32_t *retry,
+ uint32_t *expire, 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_result_t zone_signwithkey(dns_zone_t *zone, dns_secalg_t algorithm,
+ uint16_t keyid,
+ bool deleteit);
+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);
+static void zone_rekey(dns_zone_t *zone);
+static isc_result_t zone_send_securedb(dns_zone_t *zone, dns_db_t *db);
+static void setrl(isc_ratelimiter_t *rl, unsigned int *rate,
+ unsigned int value);
+
+#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; \
+ 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)
+
+typedef struct nsec3param nsec3param_t;
+struct nsec3param {
+ unsigned char data[DNS_NSEC3PARAM_BUFFERSIZE + 1];
+ unsigned int length;
+ bool nsec;
+ bool replace;
+ ISC_LINK(nsec3param_t) link;
+};
+typedef ISC_LIST(nsec3param_t) nsec3paramlist_t;
+struct np3event {
+ isc_event_t event;
+ nsec3param_t params;
+};
+
+struct ssevent {
+ isc_event_t event;
+ uint32_t serial;
+};
+
+/*%
+ * Increment resolver-related statistics counters. Zone must be locked.
+ */
+static inline void
+inc_stats(dns_zone_t *zone, isc_statscounter_t counter) {
+ if (zone->stats != NULL)
+ isc_stats_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 = 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;
+ ISC_LIST_INIT(zone->includes);
+ ISC_LIST_INIT(zone->newincludes);
+ zone->nincludes = 0;
+ zone->masterformat = dns_masterformat_none;
+ zone->masterstyle = NULL;
+ 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->options2 = 0;
+ zone->keyopts = 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);
+ isc_time_settoepoch(&zone->refreshkeytime);
+ zone->refreshkeyinterval = 0;
+ zone->refreshkeycount = 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->masterdscps = NULL;
+ zone->masterkeynames = NULL;
+ zone->mastersok = NULL;
+ zone->masterscnt = 0;
+ zone->curmaster = 0;
+ zone->maxttl = 0;
+ zone->notify = NULL;
+ zone->notifykeynames = NULL;
+ zone->notifydscp = NULL;
+ zone->notifytype = dns_notifytype_yes;
+ zone->notifycnt = 0;
+ zone->task = NULL;
+ zone->loadtask = 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 = false;
+ zone->zero_no_soa_ttl = 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;
+ zone->log_key_expired_timer = 0;
+ 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->notifysrc4dscp = -1;
+ zone->notifysrc6dscp = -1;
+ zone->xfrsource4dscp = -1;
+ zone->xfrsource6dscp = -1;
+ zone->altxfrsource4dscp = -1;
+ zone->altxfrsource6dscp = -1;
+ 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->prev_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 = false;
+ zone->statlevel = dns_zonestat_none;
+ zone->requeststats = NULL;
+ zone->rcvquerystats = NULL;
+ zone->notifydelay = 5;
+ zone->isself = NULL;
+ zone->isselfarg = NULL;
+ ISC_LIST_INIT(zone->signing);
+ ISC_LIST_INIT(zone->nsec3chain);
+ ISC_LIST_INIT(zone->setnsec3param_queue);
+ zone->signatures = 10;
+ zone->nodes = 100;
+ zone->privatetype = (dns_rdatatype_t)0xffffU;
+ zone->added = false;
+ zone->automatic = false;
+ zone->rpzs = NULL;
+ zone->rpz_num = DNS_RPZ_INVALID_NUM;
+
+ zone->catzs = NULL;
+ zone->parentcatz = NULL;
+
+ ISC_LIST_INIT(zone->forwards);
+ zone->raw = NULL;
+ zone->secure = NULL;
+ zone->sourceserial = 0;
+ zone->sourceserialset = false;
+ zone->requestixfr = true;
+ zone->requestexpire = true;
+ ISC_LIST_INIT(zone->rss_events);
+ zone->rss_db = NULL;
+ zone->rss_raw = NULL;
+ zone->rss_newver = NULL;
+ zone->rss_oldver = NULL;
+ zone->rss_event = NULL;
+ zone->rss_state = NULL;
+ zone->updatemethod = dns_updatemethod_increment;
+ zone->maxrecords = 0U;
+
+ 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;
+ isc_event_t *setnsec3param_event;
+ dns_include_t *include;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(isc_refcount_current(&zone->erefs) == 0);
+ REQUIRE(zone->irefs == 0);
+ REQUIRE(!LOCKED_ZONE(zone));
+ REQUIRE(zone->timer == NULL);
+ REQUIRE(zone->zmgr == 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->loadtask != NULL) {
+ isc_task_detach(&zone->loadtask);
+ }
+ if (zone->view != NULL) {
+ dns_view_weakdetach(&zone->view);
+ }
+ if (zone->prev_view != NULL) {
+ dns_view_weakdetach(&zone->prev_view);
+ }
+
+ /* Unmanaged objects */
+ while (!ISC_LIST_EMPTY(zone->setnsec3param_queue)) {
+ setnsec3param_event = ISC_LIST_HEAD(zone->setnsec3param_queue);
+ ISC_LIST_UNLINK(zone->setnsec3param_queue, setnsec3param_event,
+ ev_link);
+ isc_event_free(&setnsec3param_event);
+ }
+ 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);
+ }
+ for (include = ISC_LIST_HEAD(zone->includes);
+ include != NULL;
+ include = ISC_LIST_HEAD(zone->includes)) {
+ ISC_LIST_UNLINK(zone->includes, include, link);
+ isc_mem_free(zone->mctx, include->name);
+ isc_mem_put(zone->mctx, include, sizeof *include);
+ }
+ for (include = ISC_LIST_HEAD(zone->newincludes);
+ include != NULL;
+ include = ISC_LIST_HEAD(zone->newincludes)) {
+ ISC_LIST_UNLINK(zone->newincludes, include, link);
+ isc_mem_free(zone->mctx, include->name);
+ isc_mem_put(zone->mctx, include, sizeof *include);
+ }
+ 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) {
+ isc_stats_detach(&zone->stats);
+ }
+ if (zone->requeststats != NULL) {
+ isc_stats_detach(&zone->requeststats);
+ }
+ if (zone->rcvquerystats != NULL){
+ dns_stats_detach(&zone->rcvquerystats);
+ }
+ if (zone->db != NULL) {
+ zone_detachdb(zone);
+ }
+ if (zone->acache != NULL) {
+ dns_acache_detach(&zone->acache);
+ }
+ if (zone->rpzs != NULL) {
+ REQUIRE(zone->rpz_num < zone->rpzs->p.num_zones);
+ dns_rpz_detach_rpzs(&zone->rpzs);
+ zone->rpz_num = DNS_RPZ_INVALID_NUM;
+ }
+ if (zone->catzs != NULL) {
+ dns_catz_catzs_detach(&zone->catzs);
+ }
+ 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);
+}
+
+/*
+ * Returns true iff this the signed side of an inline-signing zone.
+ * Caller should hold zone lock.
+ */
+static inline bool
+inline_secure(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ if (zone->raw != NULL)
+ return (true);
+ return (false);
+}
+
+/*
+ * Returns true iff this the unsigned side of an inline-signing zone
+ * Caller should hold zone lock.
+ */
+static inline bool
+inline_raw(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ if (zone->secure != NULL)
+ return (true);
+ return (false);
+}
+
+/*
+ * 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);
+ INSIST(zone != zone->raw);
+ 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);
+
+ if (inline_secure(zone))
+ dns_zone_setclass(zone->raw, rdclass);
+ 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_result_t
+dns_zone_getserial2(dns_zone_t *zone, uint32_t *serialp) {
+ isc_result_t result;
+ unsigned int soacount;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(serialp != NULL);
+
+ LOCK_ZONE(zone);
+ ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read);
+ if (zone->db != NULL) {
+ result = zone_get_from_db(zone, zone->db, NULL, &soacount,
+ serialp, NULL, NULL, NULL, NULL,
+ NULL);
+ if (result == ISC_R_SUCCESS && soacount == 0)
+ result = ISC_R_FAILURE;
+ } else
+ result = DNS_R_NOTLOADED;
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
+ UNLOCK_ZONE(zone);
+
+ return (result);
+}
+
+uint32_t
+dns_zone_getserial(dns_zone_t *zone) {
+ isc_result_t result;
+ uint32_t serial;
+
+ result = dns_zone_getserial2(zone, &serial);
+ if (result != ISC_R_SUCCESS)
+ serial = 0; /* XXX: not really correct, but no other choice */
+
+ return (serial);
+}
+
+/*
+ * Single shot.
+ */
+void
+dns_zone_settype(dns_zone_t *zone, dns_zonetype_t type) {
+ char namebuf[1024];
+
+ 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;
+
+ 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);
+ 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, *base;
+
+ 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;
+ base = mem;
+ tmp2 += (zone->db_argc + 1) * sizeof(char *);
+ for (i = 0; i < zone->db_argc; i++) {
+ *tmp++ = tmp2;
+ strlcpy(tmp2, zone->db_argv[i], size - (tmp2 - base));
+ 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 **argv = 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. */
+ argv = isc_mem_get(zone->mctx, dbargc * sizeof(*argv));
+ if (argv == NULL) {
+ goto nomem;
+ }
+ for (i = 0; i < dbargc; i++) {
+ argv[i] = NULL;
+ }
+ for (i = 0; i < dbargc; i++) {
+ argv[i] = isc_mem_strdup(zone->mctx, dbargv[i]);
+ if (argv[i] == NULL)
+ goto nomem;
+ }
+
+ /* Free the old list. */
+ zone_freedbargs(zone);
+
+ zone->db_argc = dbargc;
+ zone->db_argv = argv;
+ result = ISC_R_SUCCESS;
+ goto unlock;
+
+ nomem:
+ if (argv != NULL) {
+ for (i = 0; i < dbargc; i++) {
+ if (argv[i] != NULL) {
+ isc_mem_free(zone->mctx, argv[i]);
+ }
+ }
+ isc_mem_put(zone->mctx, argv, dbargc * sizeof(*argv));
+ }
+ result = ISC_R_NOMEMORY;
+
+ unlock:
+ UNLOCK_ZONE(zone);
+ return (result);
+}
+
+static void
+dns_zone_setview_helper(dns_zone_t *zone, dns_view_t *view) {
+ char namebuf[1024];
+
+ if (zone->prev_view == NULL && zone->view != NULL) {
+ dns_view_weakattach(zone->view, &zone->prev_view);
+ }
+
+ INSIST(zone != zone->raw);
+ 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);
+
+ if (inline_secure(zone)) {
+ dns_zone_setview(zone->raw, view);
+ }
+}
+
+void
+dns_zone_setview(dns_zone_t *zone, dns_view_t *view) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ dns_zone_setview_helper(zone, view);
+ UNLOCK_ZONE(zone);
+}
+
+dns_view_t *
+dns_zone_getview(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return (zone->view);
+}
+
+void
+dns_zone_setviewcommit(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ if (zone->prev_view != NULL)
+ dns_view_weakdetach(&zone->prev_view);
+ UNLOCK_ZONE(zone);
+}
+
+void
+dns_zone_setviewrevert(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ if (zone->prev_view != NULL) {
+ dns_zone_setview_helper(zone, zone->prev_view);
+ dns_view_weakdetach(&zone->prev_view);
+ }
+ UNLOCK_ZONE(zone);
+}
+
+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);
+ INSIST(zone != zone->raw);
+ 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);
+
+ if (result == ISC_R_SUCCESS && inline_secure(zone))
+ result = dns_zone_setorigin(zone->raw, origin);
+ 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_setfile3(zone, file, dns_masterformat_text,
+ &dns_master_style_default));
+}
+
+isc_result_t
+dns_zone_setfile2(dns_zone_t *zone, const char *file,
+ dns_masterformat_t format)
+{
+ return (dns_zone_setfile3(zone, file, format,
+ &dns_master_style_default));
+}
+
+isc_result_t
+dns_zone_setfile3(dns_zone_t *zone, const char *file,
+ dns_masterformat_t format,
+ const dns_master_style_t *style)
+{
+ 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;
+ if (format == dns_masterformat_text)
+ zone->masterstyle = style;
+ 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);
+}
+
+dns_ttl_t
+dns_zone_getmaxttl(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return (zone->maxttl);
+}
+
+void
+dns_zone_setmaxttl(dns_zone_t *zone, dns_ttl_t maxttl) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ if (maxttl != 0)
+ zone->options2 |= DNS_ZONEOPT2_CHECKTTL;
+ else
+ zone->options2 &= ~DNS_ZONEOPT2_CHECKTTL;
+ zone->maxttl = maxttl;
+ UNLOCK_ZONE(zone);
+
+ return;
+}
+
+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);
+ strlcpy(journal, zone->masterfile, len);
+ strlcat(journal, ".jnl", len);
+ } 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 *myjournal) {
+ isc_result_t result = ISC_R_SUCCESS;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ result = dns_zone_setstring(zone, &zone->journal, myjournal);
+ 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, key 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; }".
+ */
+bool
+dns_zone_isdynamic(dns_zone_t *zone, bool ignore_freeze) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ if (zone->type == dns_zone_slave || zone->type == dns_zone_stub ||
+ zone->type == dns_zone_key ||
+ (zone->type == dns_zone_redirect && zone->masters != NULL))
+ return (true);
+
+ /* Inline zones are always dynamic. */
+ if (zone->type == dns_zone_master && zone->raw != NULL)
+ return (true);
+
+ /* If !ignore_freeze, we need check whether updates are disabled. */
+ if (zone->type == dns_zone_master &&
+ (!zone->update_disabled || ignore_freeze) &&
+ ((zone->ssutable != NULL) ||
+ (zone->update_acl != NULL && !dns_acl_isnone(zone->update_acl))))
+ return (true);
+
+ return (false);
+
+}
+
+/*
+ * Set the response policy index and information for a zone.
+ */
+isc_result_t
+dns_zone_rpz_enable(dns_zone_t *zone, dns_rpz_zones_t *rpzs,
+ dns_rpz_num_t rpz_num)
+{
+ /*
+ * Only RBTDB zones can be used for response policy zones,
+ * because only they have the code to load the create the summary data.
+ * Only zones that are loaded instead of mmap()ed create the
+ * summary data and so can be policy zones.
+ */
+ if (strcmp(zone->db_argv[0], "rbt") != 0 &&
+ strcmp(zone->db_argv[0], "rbt64") != 0)
+ return (ISC_R_NOTIMPLEMENTED);
+ if (zone->masterformat == dns_masterformat_map)
+ return (ISC_R_NOTIMPLEMENTED);
+
+ /*
+ * This must happen only once or be redundant.
+ */
+ LOCK_ZONE(zone);
+ if (zone->rpzs != NULL) {
+ REQUIRE(zone->rpzs == rpzs && zone->rpz_num == rpz_num);
+ } else {
+ REQUIRE(zone->rpz_num == DNS_RPZ_INVALID_NUM);
+ dns_rpz_attach_rpzs(rpzs, &zone->rpzs);
+ zone->rpz_num = rpz_num;
+ }
+ rpzs->defined |= DNS_RPZ_ZBIT(rpz_num);
+ UNLOCK_ZONE(zone);
+
+ return (ISC_R_SUCCESS);
+}
+
+dns_rpz_num_t
+dns_zone_get_rpz_num(dns_zone_t *zone) {
+ return (zone->rpz_num);
+}
+
+/*
+ * If a zone is a response policy zone, mark its new database.
+ */
+void
+dns_zone_rpz_enable_db(dns_zone_t *zone, dns_db_t *db) {
+ if (zone->rpz_num != DNS_RPZ_INVALID_NUM) {
+ REQUIRE(zone->rpzs != NULL);
+ dns_db_rpz_attach(db, zone->rpzs, zone->rpz_num);
+ }
+}
+
+void
+dns_zone_catz_enable(dns_zone_t *zone, dns_catz_zones_t *catzs) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(catzs != NULL);
+
+ LOCK_ZONE(zone);
+ INSIST(zone->catzs == NULL || zone->catzs == catzs);
+ dns_catz_catzs_set_view(catzs, zone->view);
+ if (zone->catzs == NULL)
+ dns_catz_catzs_attach(catzs, &zone->catzs);
+ UNLOCK_ZONE(zone);
+}
+
+/*
+ * If a zone is a catalog zone, attach it to update notification in database.
+ */
+void
+dns_zone_catz_enable_db(dns_zone_t *zone, dns_db_t *db) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(db != NULL);
+
+ if (zone->catzs != NULL) {
+ dns_db_updatenotify_register(db, dns_catz_dbupdate_callback,
+ zone->catzs);
+ }
+}
+
+/*
+ * Set catalog zone ownership of the zone
+ */
+void
+dns_zone_set_parentcatz(dns_zone_t *zone, dns_catz_zone_t *catz) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(catz != NULL);
+ LOCK_ZONE(zone);
+ INSIST(zone->parentcatz == NULL || zone->parentcatz == catz);
+ zone->parentcatz = catz;
+ UNLOCK_ZONE(zone);
+}
+
+dns_catz_zone_t *
+dns_zone_get_parentcatz(const dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ return (zone->parentcatz);
+}
+
+
+static bool
+zone_touched(dns_zone_t *zone) {
+ isc_result_t result;
+ isc_time_t modtime;
+ dns_include_t *include;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ result = isc_file_getmodtime(zone->masterfile, &modtime);
+ if (result != ISC_R_SUCCESS ||
+ isc_time_compare(&modtime, &zone->loadtime) > 0)
+ return (true);
+
+ for (include = ISC_LIST_HEAD(zone->includes);
+ include != NULL;
+ include = ISC_LIST_NEXT(include, link))
+ {
+ result = isc_file_getmodtime(include->name, &modtime);
+ if (result != ISC_R_SUCCESS ||
+ isc_time_compare(&modtime, &include->filetime) > 0)
+ return (true);
+ }
+
+ return (false);
+}
+
+/*
+ * Note: when dealing with inline-signed zones, external callers will always
+ * call zone_load() for the secure zone; zone_load() calls itself recursively
+ * in order to load the raw zone.
+ */
+static isc_result_t
+zone_load(dns_zone_t *zone, unsigned int flags, bool locked) {
+ isc_result_t result;
+ isc_time_t now;
+ isc_time_t loadtime;
+ dns_db_t *db = NULL;
+ bool rbt, hasraw;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ if (!locked)
+ LOCK_ZONE(zone);
+
+ INSIST(zone != zone->raw);
+ hasraw = inline_secure(zone);
+ if (hasraw) {
+ /*
+ * We are trying to load an inline-signed zone. First call
+ * self recursively to try loading the raw version of the zone.
+ * Assuming the raw zone file is readable, there are two
+ * possibilities:
+ *
+ * a) the raw zone was not yet loaded and thus it will be
+ * loaded now, synchronously; if this succeeds, a
+ * subsequent attempt to load the signed zone file will
+ * take place and thus zone_postload() will be called
+ * twice: first for the raw zone and then for the secure
+ * zone; the latter call will take care of syncing the raw
+ * version with the secure version,
+ *
+ * b) the raw zone was already loaded and we are trying to
+ * reload it, which will happen asynchronously; this means
+ * zone_postload() will only be called for the raw zone
+ * because "result" returned by the zone_load() call below
+ * will not be ISC_R_SUCCESS but rather DNS_R_CONTINUE;
+ * zone_postload() called for the raw zone will take care
+ * of syncing the raw version with the secure version.
+ */
+ result = zone_load(zone->raw, flags, false);
+ if (result != ISC_R_SUCCESS) {
+ if (!locked)
+ UNLOCK_ZONE(zone);
+ return(result);
+ }
+ LOCK_ZONE(zone->raw);
+ }
+
+ TIME_NOW(&now);
+
+ INSIST(zone->type != dns_zone_none);
+
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADING)) {
+ if ((flags & DNS_ZONELOADFLAG_THAW) != 0)
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_THAW);
+ result = DNS_R_CONTINUE;
+ goto cleanup;
+ }
+
+ INSIST(zone->db_argc >= 1);
+
+ rbt = strcmp(zone->db_argv[0], "rbt") == 0 ||
+ strcmp(zone->db_argv[0], "rbt64") == 0;
+
+ if (zone->db != NULL && zone->masterfile == NULL && rbt) {
+ /*
+ * The zone has no master file configured.
+ */
+ result = ISC_R_SUCCESS;
+ goto cleanup;
+ }
+
+ if (zone->db != NULL && dns_zone_isdynamic(zone, false)) {
+ /*
+ * 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 && !hasraw)
+ 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) {
+ isc_time_t filetime;
+
+ /*
+ * 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;
+ }
+
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADED) &&
+ !zone_touched(zone))
+ {
+ dns_zone_log(zone, ISC_LOG_DEBUG(1),
+ "skipping load: master file "
+ "older than last load");
+ result = DNS_R_UPTODATE;
+ goto cleanup;
+ }
+
+ /*
+ * If the file modification time is in the past
+ * set loadtime to that value.
+ */
+ result = isc_file_getmodtime(zone->masterfile, &filetime);
+ if (result == ISC_R_SUCCESS &&
+ isc_time_compare(&loadtime, &filetime) > 0)
+ loadtime = filetime;
+ }
+
+ /*
+ * Built in zones (with the exception of empty zones) don't need
+ * to be reloaded.
+ */
+ if (zone->type == dns_zone_master &&
+ strcmp(zone->db_argv[0], "_builtin") == 0 &&
+ (zone->db_argc < 2 || strcmp(zone->db_argv[1], "empty") != 0) &&
+ DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADED)) {
+ result = ISC_R_SUCCESS;
+ goto cleanup;
+ }
+
+ /*
+ * Zones associated with a DLZ don't need to be loaded either,
+ * but we need to associate the database with the zone object.
+ */
+ if (strcmp(zone->db_argv[0], "dlz") == 0) {
+ dns_dlzdb_t *dlzdb;
+ dns_dlzfindzone_t findzone;
+
+ for (dlzdb = ISC_LIST_HEAD(zone->view->dlz_unsearched);
+ dlzdb != NULL;
+ dlzdb = ISC_LIST_NEXT(dlzdb, link))
+ {
+ INSIST(DNS_DLZ_VALID(dlzdb));
+ if (strcmp(zone->db_argv[1], dlzdb->dlzname) == 0)
+ break;
+ }
+
+ if (dlzdb == NULL) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "DLZ %s does not exist or is set "
+ "to 'search yes;'", zone->db_argv[1]);
+ result = ISC_R_NOTFOUND;
+ goto cleanup;
+ }
+
+ ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_write);
+ /* ask SDLZ driver if the zone is supported */
+ findzone = dlzdb->implementation->methods->findzone;
+ result = (*findzone)(dlzdb->implementation->driverarg,
+ dlzdb->dbdata, dlzdb->mctx,
+ zone->view->rdclass, &zone->origin,
+ NULL, NULL, &db);
+ if (result != ISC_R_NOTFOUND) {
+ if (zone->db != NULL)
+ zone_detachdb(zone);
+ zone_attachdb(zone, db);
+ dns_db_detach(&db);
+ result = ISC_R_SUCCESS;
+ }
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_write);
+
+ if (result == ISC_R_SUCCESS) {
+ if (dlzdb->configure_callback == NULL)
+ goto cleanup;
+
+ result = (*dlzdb->configure_callback)(zone->view,
+ dlzdb, zone);
+ if (result != ISC_R_SUCCESS)
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "DLZ configuration callback: %s",
+ isc_result_totext(result));
+ }
+ goto cleanup;
+ }
+
+ if ((zone->type == dns_zone_slave || zone->type == dns_zone_stub ||
+ (zone->type == dns_zone_redirect && zone->masters != NULL)) &&
+ rbt) {
+ 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 ||
+ (zone->type == dns_zone_redirect &&
+ zone->masters == NULL)) {
+ 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);
+ if ((flags & DNS_ZONELOADFLAG_THAW) != 0)
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_THAW);
+ goto cleanup;
+ }
+
+ result = zone_postload(zone, db, loadtime, result);
+
+ cleanup:
+ if (hasraw)
+ UNLOCK_ZONE(zone->raw);
+ if (!locked)
+ 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, false));
+}
+
+isc_result_t
+dns_zone_loadnew(dns_zone_t *zone) {
+ return (zone_load(zone, DNS_ZONELOADFLAG_NOSTAT, false));
+}
+
+static void
+zone_asyncload(isc_task_t *task, isc_event_t *event) {
+ dns_asyncload_t *asl = event->ev_arg;
+ dns_zone_t *zone = asl->zone;
+ isc_result_t result;
+
+ UNUSED(task);
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ isc_event_free(&event);
+
+ LOCK_ZONE(zone);
+ result = zone_load(zone, asl->newonly ? DNS_ZONELOADFLAG_NOSTAT : 0,
+ true);
+ if (result != DNS_R_CONTINUE) {
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_LOADPENDING);
+ }
+ UNLOCK_ZONE(zone);
+
+ /* Inform the zone table we've finished loading */
+ if (asl->loaded != NULL)
+ (asl->loaded)(asl->loaded_arg, zone, task);
+
+ isc_mem_put(zone->mctx, asl, sizeof (*asl));
+ dns_zone_idetach(&zone);
+}
+
+isc_result_t
+dns_zone_asyncload(dns_zone_t *zone, dns_zt_zoneloaded_t done, void *arg) {
+ return (dns_zone_asyncload2(zone, done, arg, false));
+}
+
+isc_result_t
+dns_zone_asyncload2(dns_zone_t *zone, dns_zt_zoneloaded_t done, void * arg,
+ bool newonly)
+{
+ isc_event_t *e;
+ dns_asyncload_t *asl = NULL;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ if (zone->zmgr == NULL)
+ return (ISC_R_FAILURE);
+
+ /* If we already have a load pending, stop now */
+ LOCK_ZONE(zone);
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADPENDING)) {
+ UNLOCK_ZONE(zone);
+ return (ISC_R_ALREADYRUNNING);
+ }
+
+ asl = isc_mem_get(zone->mctx, sizeof (*asl));
+ if (asl == NULL)
+ CHECK(ISC_R_NOMEMORY);
+
+ asl->zone = NULL;
+ asl->loaded = done;
+ asl->loaded_arg = arg;
+ asl->newonly = newonly;
+
+ e = isc_event_allocate(zone->zmgr->mctx, zone->zmgr,
+ DNS_EVENT_ZONELOAD,
+ zone_asyncload, asl,
+ sizeof(isc_event_t));
+ if (e == NULL)
+ CHECK(ISC_R_NOMEMORY);
+
+ zone_iattach(zone, &asl->zone);
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADPENDING);
+ isc_task_send(zone->loadtask, &e);
+ UNLOCK_ZONE(zone);
+
+ return (ISC_R_SUCCESS);
+
+ failure:
+ if (asl != NULL)
+ isc_mem_put(zone->mctx, asl, sizeof (*asl));
+ UNLOCK_ZONE(zone);
+ return (result);
+}
+
+bool
+dns__zone_loadpending(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADPENDING));
+}
+
+isc_result_t
+dns_zone_loadandthaw(dns_zone_t *zone) {
+ isc_result_t result;
+
+ if (inline_raw(zone))
+ result = zone_load(zone->secure, DNS_ZONELOADFLAG_THAW,
+ false);
+ else
+ result = zone_load(zone, DNS_ZONELOADFLAG_THAW, false);
+
+ switch (result) {
+ case DNS_R_CONTINUE:
+ /* Deferred thaw. */
+ break;
+ case DNS_R_UPTODATE:
+ case ISC_R_SUCCESS:
+ case DNS_R_SEENINCLUDE:
+ zone->update_disabled = false;
+ break;
+ case DNS_R_NOMASTERFILE:
+ zone->update_disabled = false;
+ break;
+ default:
+ /* Error, remain in disabled state. */
+ break;
+ }
+ return (result);
+}
+
+static unsigned int
+get_master_options(dns_zone_t *zone) {
+ unsigned int options;
+
+ options = DNS_MASTER_ZONE | DNS_MASTER_RESIGN;
+ if (zone->type == dns_zone_slave ||
+ (zone->type == dns_zone_redirect && zone->masters == NULL))
+ options |= DNS_MASTER_SLAVE;
+ if (zone->type == dns_zone_key)
+ options |= DNS_MASTER_KEY;
+ 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 (DNS_ZONE_OPTION2(zone, DNS_ZONEOPT2_CHECKTTL))
+ options |= DNS_MASTER_CHECKTTL;
+ return (options);
+}
+
+static void
+zone_registerinclude(const char *filename, void *arg) {
+ isc_result_t result;
+ dns_zone_t *zone = (dns_zone_t *) arg;
+ dns_include_t *inc = NULL;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ if (filename == NULL)
+ return;
+
+ /*
+ * Suppress duplicates.
+ */
+ for (inc = ISC_LIST_HEAD(zone->newincludes);
+ inc != NULL;
+ inc = ISC_LIST_NEXT(inc, link))
+ if (strcmp(filename, inc->name) == 0)
+ return;
+
+ inc = isc_mem_get(zone->mctx, sizeof(dns_include_t));
+ if (inc == NULL)
+ return;
+ inc->name = isc_mem_strdup(zone->mctx, filename);
+ if (inc->name == NULL) {
+ isc_mem_put(zone->mctx, inc, sizeof(dns_include_t));
+ return;
+ }
+ ISC_LINK_INIT(inc, link);
+
+ result = isc_file_getmodtime(filename, &inc->filetime);
+ if (result != ISC_R_SUCCESS)
+ isc_time_settoepoch(&inc->filetime);
+
+ ISC_LIST_APPEND(zone->newincludes, inc, link);
+}
+
+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_loadfileinc5(load->zone->masterfile,
+ dns_db_origin(load->db),
+ dns_db_origin(load->db),
+ load->zone->rdclass, options, 0,
+ &load->callbacks, task,
+ zone_loaddone, load,
+ &load->zone->lctx,
+ zone_registerinclude,
+ load->zone, load->zone->mctx,
+ load->zone->masterformat,
+ load->zone->maxttl);
+ if (result != ISC_R_SUCCESS && result != DNS_R_CONTINUE &&
+ result != DNS_R_SEENINCLUDE)
+ goto fail;
+ return;
+
+ fail:
+ zone_loaddone(load, result);
+}
+
+static void
+get_raw_serial(dns_zone_t *raw, dns_masterrawheader_t *rawdata) {
+ isc_result_t result;
+ unsigned int soacount;
+
+ LOCK(&raw->lock);
+ if (raw->db != NULL) {
+ result = zone_get_from_db(raw, raw->db, NULL, &soacount,
+ &rawdata->sourceserial,
+ NULL, NULL, NULL, NULL,
+ NULL);
+ if (result == ISC_R_SUCCESS && soacount > 0U)
+ rawdata->flags |= DNS_MASTERRAW_SOURCESERIALSET;
+ }
+ UNLOCK(&raw->lock);
+}
+
+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;
+ dns_masterrawheader_t rawdata;
+
+ 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);
+ INSIST(zone != zone->raw);
+ ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read);
+ if (zone->db != NULL) {
+ const dns_master_style_t *output_style;
+
+ dns_db_currentversion(zone->db, &version);
+ dns_master_initrawheader(&rawdata);
+ if (inline_secure(zone))
+ get_raw_serial(zone->raw, &rawdata);
+ if (zone->type == dns_zone_key)
+ output_style = &dns_master_style_keyzone;
+ else if (zone->masterstyle != NULL)
+ output_style = zone->masterstyle;
+ else
+ output_style = &dns_master_style_default;
+ result = dns_master_dumpinc3(zone->mctx, zone->db, version,
+ output_style, zone->masterfile,
+ zone->task, dump_done, zone, &zone->dctx, zone->masterformat,
+ &rawdata);
+ dns_db_closeversion(zone->db, &version, false);
+ } else
+ result = ISC_R_CANCELED;
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
+ UNLOCK_ZONE(zone);
+ if (result != DNS_R_CONTINUE)
+ goto fail;
+ return;
+
+ fail:
+ dump_done(zone, result);
+}
+
+/*
+ * Save the raw serial number for inline-signing zones.
+ * (XXX: Other information from the header will be used
+ * for other purposes in the future, but for now this is
+ * all we're interested in.)
+ */
+static void
+zone_setrawdata(dns_zone_t *zone, dns_masterrawheader_t *header) {
+ if ((header->flags & DNS_MASTERRAW_SOURCESERIALSET) == 0)
+ return;
+
+ zone->sourceserial = header->sourceserial;
+ zone->sourceserialset = true;
+}
+
+void
+dns_zone_setrawdata(dns_zone_t *zone, dns_masterrawheader_t *header) {
+ if (zone == NULL)
+ return;
+
+ LOCK_ZONE(zone);
+ zone_setrawdata(zone, header);
+ UNLOCK_ZONE(zone);
+}
+
+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;
+
+ dns_zone_rpz_enable_db(zone, db);
+ dns_zone_catz_enable_db(zone, db);
+ 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->loadtask != 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);
+ load->callbacks.rawdata = zone_setrawdata;
+ zone_iattach(zone, &load->callbacks.zone);
+ result = dns_db_beginload(db, &load->callbacks);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ result = zonemgr_getio(zone->zmgr, true, zone->loadtask,
+ 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);
+ goto cleanup;
+ } else
+ result = DNS_R_CONTINUE;
+ } else {
+ dns_rdatacallbacks_t callbacks;
+
+ dns_rdatacallbacks_init(&callbacks);
+ callbacks.rawdata = zone_setrawdata;
+ zone_iattach(zone, &callbacks.zone);
+ result = dns_db_beginload(db, &callbacks);
+ if (result != ISC_R_SUCCESS) {
+ zone_idetach(&callbacks.zone);
+ return (result);
+ }
+ result = dns_master_loadfile5(zone->masterfile,
+ &zone->origin, &zone->origin,
+ zone->rdclass, options, 0,
+ &callbacks,
+ zone_registerinclude,
+ zone, zone->mctx,
+ zone->masterformat,
+ zone->maxttl);
+ tresult = dns_db_endload(db, &callbacks);
+ if (result == ISC_R_SUCCESS)
+ result = tresult;
+ zone_idetach(&callbacks.zone);
+ }
+
+ return (result);
+
+ cleanup:
+ load->magic = 0;
+ dns_db_detach(&load->db);
+ zone_idetach(&load->zone);
+ zone_idetach(&load->callbacks.zone);
+ isc_mem_detach(&load->mctx);
+ isc_mem_put(zone->mctx, load, sizeof(*load));
+ return (result);
+}
+
+static bool
+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;
+
+ /*
+ * "." means the services does not exist.
+ */
+ if (dns_name_equal(name, dns_rootname))
+ return (true);
+
+ /*
+ * Outside of zone.
+ */
+ if (!dns_name_issubdomain(name, &zone->origin)) {
+ if (zone->checkmx != NULL)
+ return ((zone->checkmx)(zone, name, owner));
+ return (true);
+ }
+
+ if (zone->type == dns_zone_master)
+ level = ISC_LOG_ERROR;
+ else
+ level = ISC_LOG_WARNING;
+
+ foundname = dns_fixedname_initname(&fixed);
+
+ result = dns_db_find(db, name, NULL, dns_rdatatype_a,
+ 0, 0, NULL, foundname, NULL, NULL);
+ if (result == ISC_R_SUCCESS)
+ return (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 (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) {
+ if (!DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKMXFAIL))
+ level = ISC_LOG_WARNING;
+ dns_zone_log(zone, level,
+ "%s/MX '%s' has no address records (A or AAAA)",
+ ownerbuf, namebuf);
+ return ((level == ISC_LOG_WARNING) ? true : false);
+ }
+
+ 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) ? true : 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) ? true : false);
+ }
+
+ if (zone->checkmx != NULL && result == DNS_R_DELEGATION)
+ return ((zone->checkmx)(zone, name, owner));
+
+ return (true);
+}
+
+static bool
+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 (true);
+
+ /*
+ * Outside of zone.
+ */
+ if (!dns_name_issubdomain(name, &zone->origin)) {
+ if (zone->checksrv != NULL)
+ return ((zone->checksrv)(zone, name, owner));
+ return (true);
+ }
+
+ if (zone->type == dns_zone_master)
+ level = ISC_LOG_ERROR;
+ else
+ level = ISC_LOG_WARNING;
+
+ foundname = dns_fixedname_initname(&fixed);
+
+ result = dns_db_find(db, name, NULL, dns_rdatatype_a,
+ 0, 0, NULL, foundname, NULL, NULL);
+ if (result == ISC_R_SUCCESS)
+ return (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 (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 (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) ? true : 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) ? true : false);
+ }
+
+ if (zone->checksrv != NULL && result == DNS_R_DELEGATION)
+ return ((zone->checksrv)(zone, name, owner));
+
+ return (true);
+}
+
+static bool
+zone_check_glue(dns_zone_t *zone, dns_db_t *db, dns_name_t *name,
+ dns_name_t *owner)
+{
+ bool answer = 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 (true);
+ }
+
+ if (zone->type == dns_zone_master)
+ level = ISC_LOG_ERROR;
+ else
+ level = ISC_LOG_WARNING;
+
+ foundname = dns_fixedname_initname(&fixed);
+ dns_rdataset_init(&a);
+ dns_rdataset_init(&aaaa);
+
+ /*
+ * Perform a regular lookup to catch DNAME records then look
+ * for glue.
+ */
+ result = dns_db_find(db, name, NULL, dns_rdatatype_a,
+ 0, 0, NULL, foundname, &a, NULL);
+ switch (result) {
+ case ISC_R_SUCCESS:
+ case DNS_R_DNAME:
+ case DNS_R_CNAME:
+ break;
+ default:
+ if (dns_rdataset_isassociated(&a))
+ dns_rdataset_disassociate(&a);
+ 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 (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) {
+ if (dns_rdataset_isassociated(&a))
+ dns_rdataset_disassociate(&a);
+ dns_rdataset_disassociate(&aaaa);
+ return (true);
+ }
+ if (tresult == DNS_R_DELEGATION || tresult == DNS_R_DNAME)
+ 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);
+ }
+ }
+
+ 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;
+ bool required = false;
+ if (dns_name_issubdomain(name, owner)) {
+ what = "REQUIRED GLUE ";
+ required = 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 = 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 = 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 = false; */
+ }
+
+ if (dns_rdataset_isassociated(&a))
+ dns_rdataset_disassociate(&a);
+ if (dns_rdataset_isassociated(&aaaa))
+ dns_rdataset_disassociate(&aaaa);
+ return (answer);
+}
+
+static bool
+zone_rrset_check_dup(dns_zone_t *zone, dns_name_t *owner,
+ dns_rdataset_t *rdataset)
+{
+ dns_rdataset_t tmprdataset;
+ isc_result_t result;
+ bool answer = true;
+ bool format = true;
+ int level = ISC_LOG_WARNING;
+ char ownerbuf[DNS_NAME_FORMATSIZE];
+ char typebuf[DNS_RDATATYPE_FORMATSIZE];
+ unsigned int count1 = 0;
+
+ if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKDUPRRFAIL))
+ level = ISC_LOG_ERROR;
+
+ dns_rdataset_init(&tmprdataset);
+ for (result = dns_rdataset_first(rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(rdataset)) {
+ dns_rdata_t rdata1 = DNS_RDATA_INIT;
+ unsigned int count2 = 0;
+
+ count1++;
+ dns_rdataset_current(rdataset, &rdata1);
+ dns_rdataset_clone(rdataset, &tmprdataset);
+ for (result = dns_rdataset_first(&tmprdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&tmprdataset)) {
+ dns_rdata_t rdata2 = DNS_RDATA_INIT;
+ count2++;
+ if (count1 >= count2)
+ continue;
+ dns_rdataset_current(&tmprdataset, &rdata2);
+ if (dns_rdata_casecompare(&rdata1, &rdata2) == 0) {
+ if (format) {
+ dns_name_format(owner, ownerbuf,
+ sizeof ownerbuf);
+ dns_rdatatype_format(rdata1.type,
+ typebuf,
+ sizeof(typebuf));
+ format = false;
+ }
+ dns_zone_log(zone, level, "%s/%s has "
+ "semantically identical records",
+ ownerbuf, typebuf);
+ if (level == ISC_LOG_ERROR)
+ answer = false;
+ break;
+ }
+ }
+ dns_rdataset_disassociate(&tmprdataset);
+ if (!format)
+ break;
+ }
+ return (answer);
+}
+
+static bool
+zone_check_dup(dns_zone_t *zone, dns_db_t *db) {
+ dns_dbiterator_t *dbiterator = NULL;
+ dns_dbnode_t *node = NULL;
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+ dns_rdataset_t rdataset;
+ dns_rdatasetiter_t *rdsit = NULL;
+ bool ok = true;
+ isc_result_t result;
+
+ name = dns_fixedname_initname(&fixed);
+ dns_rdataset_init(&rdataset);
+
+ result = dns_db_createiterator(db, 0, &dbiterator);
+ if (result != ISC_R_SUCCESS)
+ return (true);
+
+ for (result = dns_dbiterator_first(dbiterator);
+ result == ISC_R_SUCCESS;
+ result = dns_dbiterator_next(dbiterator)) {
+ result = dns_dbiterator_current(dbiterator, &node, name);
+ if (result != ISC_R_SUCCESS)
+ continue;
+
+ result = dns_db_allrdatasets(db, node, NULL, 0, &rdsit);
+ if (result != ISC_R_SUCCESS)
+ continue;
+
+ for (result = dns_rdatasetiter_first(rdsit);
+ result == ISC_R_SUCCESS;
+ result = dns_rdatasetiter_next(rdsit)) {
+ dns_rdatasetiter_current(rdsit, &rdataset);
+ if (!zone_rrset_check_dup(zone, name, &rdataset))
+ ok = false;
+ dns_rdataset_disassociate(&rdataset);
+ }
+ dns_rdatasetiter_destroy(&rdsit);
+ dns_db_detachnode(db, &node);
+ }
+
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ dns_dbiterator_destroy(&dbiterator);
+
+ return (ok);
+}
+
+static bool
+isspf(const dns_rdata_t *rdata) {
+ char buf[1024];
+ const unsigned char *data = rdata->data;
+ unsigned int rdl = rdata->length, i = 0, tl, len;
+
+ while (rdl > 0U) {
+ len = tl = *data;
+ ++data;
+ --rdl;
+ INSIST(tl <= rdl);
+ if (len > sizeof(buf) - i - 1)
+ len = sizeof(buf) - i - 1;
+ memmove(buf + i, data, len);
+ i += len;
+ data += tl;
+ rdl -= tl;
+ }
+
+ if (i < 6U)
+ return(false);
+
+ buf[i] = 0;
+ if (strncmp(buf, "v=spf1", 6) == 0 && (buf[6] == 0 || buf[6] == ' '))
+ return (true);
+ return (false);
+}
+
+static bool
+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;
+ bool ok = true, have_spf, have_txt;
+
+ name = dns_fixedname_initname(&fixed);
+ bottom = dns_fixedname_initname(&fixedbottom);
+ dns_rdataset_init(&rdataset);
+ dns_rdata_init(&rdata);
+
+ result = dns_db_createiterator(db, 0, &dbiterator);
+ if (result != ISC_R_SUCCESS)
+ return (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 checkfordname;
+
+ result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_ns,
+ 0, 0, &rdataset, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto checkfordname;
+ /*
+ * Remember bottom of zone due to NS.
+ */
+ 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 = false;
+ dns_rdata_reset(&rdata);
+ result = dns_rdataset_next(&rdataset);
+ }
+ dns_rdataset_disassociate(&rdataset);
+ goto next;
+
+ checkfordname:
+ result = dns_db_findrdataset(db, node, NULL,
+ dns_rdatatype_dname, 0, 0,
+ &rdataset, NULL);
+ if (result == ISC_R_SUCCESS) {
+ /*
+ * Remember bottom of zone due to DNAME.
+ */
+ dns_name_copy(name, bottom, NULL);
+ dns_rdataset_disassociate(&rdataset);
+ }
+
+ 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 = 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 checkspf;
+ 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 = false;
+ dns_rdata_reset(&rdata);
+ result = dns_rdataset_next(&rdataset);
+ }
+ dns_rdataset_disassociate(&rdataset);
+
+ checkspf:
+ /*
+ * Check if there is a type SPF record without an
+ * SPF-formatted type TXT record also being present.
+ */
+ if (!DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKSPF))
+ goto next;
+ if (zone->rdclass != dns_rdataclass_in)
+ goto next;
+ have_spf = have_txt = false;
+ result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_spf,
+ 0, 0, &rdataset, NULL);
+ if (result == ISC_R_SUCCESS) {
+ dns_rdataset_disassociate(&rdataset);
+ have_spf = true;
+ }
+ result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_txt,
+ 0, 0, &rdataset, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto notxt;
+ result = dns_rdataset_first(&rdataset);
+ while (result == ISC_R_SUCCESS) {
+ dns_rdataset_current(&rdataset, &rdata);
+ have_txt = isspf(&rdata);
+ dns_rdata_reset(&rdata);
+ if (have_txt)
+ break;
+ result = dns_rdataset_next(&rdataset);
+ }
+ dns_rdataset_disassociate(&rdataset);
+
+ notxt:
+ if (have_spf && !have_txt) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+
+ dns_name_format(name, namebuf, sizeof(namebuf));
+ dns_zone_log(zone, ISC_LOG_WARNING, "'%s' found type "
+ "SPF record but no SPF TXT record found, "
+ "add matching type TXT record", namebuf);
+ }
+
+ 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;
+ bool logit, foundrsa = false, foundmd5 = false;
+ const char *algorithm;
+
+ result = dns_db_findnode(db, &zone->origin, 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 = true;
+ algorithm = "RSASHA1";
+ } else {
+ logit = !foundmd5;
+ foundmd5 = 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, 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;
+ dns_db_t *db = NULL;
+
+ 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)
+ goto cleanup;
+
+ result = dns_db_findnode(db, &zone->origin, 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,
+ zone->privatetype,
+ dns_rdatatype_none, 0,
+ &rdataset, NULL);
+ if (result != ISC_R_SUCCESS) {
+ INSIST(!dns_rdataset_isassociated(&rdataset));
+ 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[0] == 0 || rdata.data[4] != 0) {
+ dns_rdata_reset(&rdata);
+ continue;
+ }
+
+ result = zone_signwithkey(zone, rdata.data[0],
+ (rdata.data[1] << 8) | rdata.data[2],
+ 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 (db != NULL) {
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ if (version != NULL)
+ dns_db_closeversion(db, &version, false);
+ dns_db_detach(&db);
+ }
+}
+
+/*
+ * Initiate adding/removing NSEC3 records belonging to the chain defined by the
+ * supplied NSEC3PARAM RDATA.
+ *
+ * Zone must be locked by caller.
+ */
+static isc_result_t
+zone_addnsec3chain(dns_zone_t *zone, dns_rdata_nsec3param_t *nsec3param) {
+ dns_nsec3chain_t *nsec3chain, *current;
+ dns_dbversion_t *version = NULL;
+ bool nseconly = false, nsec3ok = false;
+ isc_result_t result;
+ isc_time_t now;
+ unsigned int options = 0;
+ char saltbuf[255*2+1];
+ char flags[sizeof("INITIAL|REMOVE|CREATE|NONSEC|OPTOUT")];
+ dns_db_t *db = NULL;
+
+ 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) {
+ result = ISC_R_SUCCESS;
+ goto cleanup;
+ }
+
+ /*
+ * If this zone is not NSEC3-capable, attempting to remove any NSEC3
+ * chain from it is pointless as it would not be possible for the
+ * latter to exist in the first place.
+ */
+ dns_db_currentversion(db, &version);
+ result = dns_nsec_nseconly(db, version, &nseconly);
+ nsec3ok = (result == ISC_R_SUCCESS && !nseconly);
+ dns_db_closeversion(db, &version, false);
+ if (!nsec3ok && (nsec3param->flags & DNS_NSEC3FLAG_REMOVE) == 0) {
+ result = ISC_R_SUCCESS;
+ goto cleanup;
+ }
+
+ /*
+ * Allocate and initialize structure preserving state of
+ * adding/removing records belonging to this NSEC3 chain between
+ * separate zone_nsec3chain() calls.
+ */
+ nsec3chain = isc_mem_get(zone->mctx, sizeof *nsec3chain);
+ if (nsec3chain == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+
+ nsec3chain->magic = 0;
+ nsec3chain->done = 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;
+ memmove(nsec3chain->salt, nsec3param->salt, nsec3param->salt_length);
+ nsec3chain->nsec3param.salt = nsec3chain->salt;
+ nsec3chain->seen_nsec = false;
+ nsec3chain->delete_nsec = false;
+ nsec3chain->save_delete_nsec = false;
+
+ /*
+ * Log NSEC3 parameters defined by supplied NSEC3PARAM RDATA.
+ */
+ if (nsec3param->flags == 0)
+ strlcpy(flags, "NONE", sizeof(flags));
+ else {
+ flags[0] = '\0';
+ if (nsec3param->flags & DNS_NSEC3FLAG_REMOVE)
+ strlcat(flags, "REMOVE", sizeof(flags));
+ if (nsec3param->flags & DNS_NSEC3FLAG_INITIAL) {
+ if (flags[0] == '\0')
+ strlcpy(flags, "INITIAL", sizeof(flags));
+ else
+ strlcat(flags, "|INITIAL", sizeof(flags));
+ }
+ if (nsec3param->flags & DNS_NSEC3FLAG_CREATE) {
+ if (flags[0] == '\0')
+ strlcpy(flags, "CREATE", sizeof(flags));
+ else
+ strlcat(flags, "|CREATE", sizeof(flags));
+ }
+ if (nsec3param->flags & DNS_NSEC3FLAG_NONSEC) {
+ if (flags[0] == '\0')
+ strlcpy(flags, "NONSEC", sizeof(flags));
+ else
+ strlcat(flags, "|NONSEC", sizeof(flags));
+ }
+ if (nsec3param->flags & DNS_NSEC3FLAG_OPTOUT) {
+ if (flags[0] == '\0')
+ strlcpy(flags, "OPTOUT", sizeof(flags));
+ else
+ strlcat(flags, "|OPTOUT", sizeof(flags));
+ }
+ }
+ result = dns_nsec3param_salttotext(nsec3param, saltbuf,
+ sizeof(saltbuf));
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ dns_zone_log(zone, ISC_LOG_INFO,
+ "zone_addnsec3chain(%u,%s,%u,%s)",
+ nsec3param->hash, flags, nsec3param->iterations,
+ saltbuf);
+
+ /*
+ * If the NSEC3 chain defined by the supplied NSEC3PARAM RDATA is
+ * currently being processed, interrupt its processing to avoid
+ * simultaneously adding and removing records for the same NSEC3 chain.
+ */
+ for (current = ISC_LIST_HEAD(zone->nsec3chain);
+ current != NULL;
+ current = ISC_LIST_NEXT(current, link)) {
+ if (current->db == 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 = true;
+ }
+
+ /*
+ * Attach zone database to the structure initialized above and create
+ * an iterator for it with appropriate options in order to avoid
+ * creating NSEC3 records for NSEC3 records.
+ */
+ dns_db_attach(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)
+ result = dns_dbiterator_first(nsec3chain->dbiterator);
+ if (result == ISC_R_SUCCESS) {
+ /*
+ * Database iterator initialization succeeded. We are now
+ * ready to kick off adding/removing records belonging to this
+ * NSEC3 chain. Append the structure initialized above to the
+ * "nsec3chain" list for the zone and set the appropriate zone
+ * timer so that zone_nsec3chain() is called as soon as
+ * possible.
+ */
+ 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);
+ }
+ }
+
+ 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);
+ }
+
+ cleanup:
+ if (db != NULL)
+ dns_db_detach(&db);
+ return (result);
+}
+
+/*
+ * Find private-type records at the zone apex which signal that an NSEC3 chain
+ * should be added or removed. For each such record, extract NSEC3PARAM RDATA
+ * and pass it to zone_addnsec3chain().
+ *
+ * Zone must be locked by caller.
+ */
+static void
+resume_addnsec3chain(dns_zone_t *zone) {
+ dns_dbnode_t *node = NULL;
+ dns_dbversion_t *version = NULL;
+ dns_rdataset_t rdataset;
+ isc_result_t result;
+ dns_rdata_nsec3param_t nsec3param;
+ bool nseconly = false, nsec3ok = false;
+ dns_db_t *db = NULL;
+
+ INSIST(LOCKED_ZONE(zone));
+
+ if (zone->privatetype == 0)
+ return;
+
+ 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)
+ goto cleanup;
+
+ result = dns_db_findnode(db, &zone->origin, false, &node);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ dns_db_currentversion(db, &version);
+
+ /*
+ * In order to create NSEC3 chains we need the DNSKEY RRset at zone
+ * apex to exist and contain no keys using NSEC-only algorithms.
+ */
+ result = dns_nsec_nseconly(db, version, &nseconly);
+ nsec3ok = (result == ISC_R_SUCCESS && !nseconly);
+
+ /*
+ * Get the RRset containing all private-type records at the zone apex.
+ */
+ dns_rdataset_init(&rdataset);
+ result = dns_db_findrdataset(db, node, version,
+ zone->privatetype, dns_rdatatype_none,
+ 0, &rdataset, NULL);
+ if (result != ISC_R_SUCCESS) {
+ INSIST(!dns_rdataset_isassociated(&rdataset));
+ goto cleanup;
+ }
+
+ for (result = dns_rdataset_first(&rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rdataset))
+ {
+ unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE];
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_t private = DNS_RDATA_INIT;
+
+ dns_rdataset_current(&rdataset, &private);
+ /*
+ * Try extracting NSEC3PARAM RDATA from this private-type
+ * record. Failure means this private-type record does not
+ * represent an NSEC3PARAM record, so skip it.
+ */
+ if (!dns_nsec3param_fromprivate(&private, &rdata, buf,
+ sizeof(buf)))
+ continue;
+ result = dns_rdata_tostruct(&rdata, &nsec3param, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ if (((nsec3param.flags & DNS_NSEC3FLAG_REMOVE) != 0) ||
+ ((nsec3param.flags & DNS_NSEC3FLAG_CREATE) != 0 && nsec3ok))
+ {
+ /*
+ * Pass the NSEC3PARAM RDATA contained in this
+ * private-type record to zone_addnsec3chain() so that
+ * it can kick off adding or removing NSEC3 records.
+ */
+ 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_rdataset_disassociate(&rdataset);
+ cleanup:
+ if (db != NULL) {
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ if (version != NULL)
+ dns_db_closeversion(db, &version, false);
+ dns_db_detach(&db);
+ }
+}
+
+static void
+set_resigntime(dns_zone_t *zone) {
+ dns_rdataset_t rdataset;
+ dns_fixedname_t fixed;
+ unsigned int resign;
+ isc_result_t result;
+ uint32_t nanosecs;
+ dns_db_t *db = NULL;
+
+ /* We only re-sign zones that can be dynamically updated */
+ if (zone->update_disabled)
+ return;
+
+ if (!inline_secure(zone) && (zone->type != dns_zone_master ||
+ (zone->ssutable == NULL &&
+ (zone->update_acl == NULL || dns_acl_isnone(zone->update_acl)))))
+ return;
+
+ dns_rdataset_init(&rdataset);
+ dns_fixedname_init(&fixed);
+
+ 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) {
+ isc_time_settoepoch(&zone->resigntime);
+ return;
+ }
+
+ result = dns_db_getsigningtime(db, &rdataset,
+ dns_fixedname_name(&fixed));
+ if (result != ISC_R_SUCCESS) {
+ isc_time_settoepoch(&zone->resigntime);
+ goto cleanup;
+ }
+
+ resign = rdataset.resign - zone->sigresigninginterval;
+ dns_rdataset_disassociate(&rdataset);
+ isc_random_get(&nanosecs);
+ nanosecs %= 1000000000;
+ isc_time_set(&zone->resigntime, resign, nanosecs);
+ cleanup:
+ dns_db_detach(&db);
+ return;
+}
+
+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;
+ bool ok = false;
+ isc_result_t result;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ bool dynamic = (zone->type == dns_zone_master) ?
+ dns_zone_isdynamic(zone, false) : false;
+
+ dns_rdataset_init(&rdataset);
+ result = dns_db_findnode(db, &zone->origin, 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) {
+ INSIST(!dns_rdataset_isassociated(&rdataset));
+ result = ISC_R_SUCCESS;
+ goto cleanup;
+ }
+ if (result != ISC_R_SUCCESS) {
+ INSIST(!dns_rdataset_isassociated(&rdataset));
+ 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 = 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 = true;
+ break;
+ } else
+ dns_zone_log(zone, ISC_LOG_WARNING,
+ "unsupported nsec3 hash algorithm: %u",
+ nsec3param.hash);
+ } else
+ ok = 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, false);
+ dns_db_detachnode(db, &node);
+ return (result);
+}
+
+/*
+ * Set the timer for refreshing the key zone to the soonest future time
+ * of the set (current timer, keydata->refresh, keydata->addhd,
+ * keydata->removehd).
+ */
+static void
+set_refreshkeytimer(dns_zone_t *zone, dns_rdata_keydata_t *key,
+ isc_stdtime_t now, bool force)
+{
+ const char me[] = "set_refreshkeytimer";
+ isc_stdtime_t then;
+ isc_time_t timenow, timethen;
+ char timebuf[80];
+
+ ENTER;
+ then = key->refresh;
+ if (force)
+ then = now;
+ if (key->addhd > now && key->addhd < then)
+ then = key->addhd;
+ if (key->removehd > now && key->removehd < then)
+ then = key->removehd;
+
+ TIME_NOW(&timenow);
+ if (then > now)
+ DNS_ZONE_TIME_ADD(&timenow, then - now, &timethen);
+ else
+ timethen = timenow;
+ if (isc_time_compare(&zone->refreshkeytime, &timenow) < 0 ||
+ isc_time_compare(&timethen, &zone->refreshkeytime) < 0)
+ zone->refreshkeytime = timethen;
+
+ isc_time_formattimestamp(&zone->refreshkeytime, timebuf, 80);
+ dns_zone_log(zone, ISC_LOG_DEBUG(1), "next key refresh: %s", timebuf);
+ zone_settimer(zone, &timenow);
+}
+
+/*
+ * Convert key(s) linked from 'keynode' to KEYDATA and add to the key zone.
+ * If the key zone is changed, set '*changed' to true.
+ */
+static isc_result_t
+create_keydata(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver,
+ dns_diff_t *diff, dns_keytable_t *keytable,
+ dns_keynode_t **keynodep, bool *changed)
+{
+ const char me[] = "create_keydata";
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_buffer_t keyb, dstb;
+ unsigned char key_buf[4096], dst_buf[DST_KEY_MAXSIZE];
+ dns_rdata_keydata_t keydata;
+ dns_rdata_dnskey_t dnskey;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_keynode_t *keynode;
+ isc_stdtime_t now;
+ isc_region_t r;
+ dst_key_t *key;
+
+ REQUIRE(keynodep != NULL);
+ keynode = *keynodep;
+
+ ENTER;
+ isc_stdtime_get(&now);
+
+ /* Loop in case there's more than one key. */
+ while (result == ISC_R_SUCCESS) {
+ dns_keynode_t *nextnode = NULL;
+
+ key = dns_keynode_key(keynode);
+ if (key == NULL)
+ goto skip;
+
+ isc_buffer_init(&dstb, dst_buf, sizeof(dst_buf));
+ CHECK(dst_key_todns(key, &dstb));
+
+ /* Convert DST key to DNSKEY. */
+ dns_rdata_reset(&rdata);
+ isc_buffer_usedregion(&dstb, &r);
+ dns_rdata_fromregion(&rdata, dst_key_class(key),
+ dns_rdatatype_dnskey, &r);
+
+ /* DSTKEY to KEYDATA. */
+ CHECK(dns_rdata_tostruct(&rdata, &dnskey, NULL));
+ CHECK(dns_keydata_fromdnskey(&keydata, &dnskey, now, 0, 0,
+ NULL));
+
+ /* KEYDATA to rdata. */
+ dns_rdata_reset(&rdata);
+ isc_buffer_init(&keyb, key_buf, sizeof(key_buf));
+ CHECK(dns_rdata_fromstruct(&rdata,
+ zone->rdclass, dns_rdatatype_keydata,
+ &keydata, &keyb));
+
+ /* Add rdata to zone. */
+ CHECK(update_one_rr(db, ver, diff, DNS_DIFFOP_ADD,
+ dst_key_name(key), 0, &rdata));
+ *changed = true;
+
+ /* Refresh new keys from the zone apex as soon as possible. */
+ set_refreshkeytimer(zone, &keydata, now, true);
+
+ skip:
+ result = dns_keytable_nextkeynode(keytable, keynode, &nextnode);
+ if (result != ISC_R_NOTFOUND) {
+ dns_keytable_detachkeynode(keytable, &keynode);
+ keynode = nextnode;
+ }
+ }
+
+ if (keynode != NULL)
+ dns_keytable_detachkeynode(keytable, &keynode);
+ *keynodep = NULL;
+
+ return (ISC_R_SUCCESS);
+
+ failure:
+ return (result);
+}
+
+/*
+ * Remove from the key zone all the KEYDATA records found in rdataset.
+ */
+static isc_result_t
+delete_keydata(dns_db_t *db, dns_dbversion_t *ver, dns_diff_t *diff,
+ dns_name_t *name, dns_rdataset_t *rdataset)
+{
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_result_t result, uresult;
+
+ for (result = dns_rdataset_first(rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(rdataset)) {
+ dns_rdata_reset(&rdata);
+ dns_rdataset_current(rdataset, &rdata);
+ uresult = update_one_rr(db, ver, diff, DNS_DIFFOP_DEL,
+ name, 0, &rdata);
+ if (uresult != ISC_R_SUCCESS)
+ return (uresult);
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ return (result);
+}
+
+/*
+ * Compute the DNSSEC key ID for a DNSKEY record.
+ */
+static isc_result_t
+compute_tag(dns_name_t *name, dns_rdata_dnskey_t *dnskey, isc_mem_t *mctx,
+ dns_keytag_t *tag)
+{
+ isc_result_t result;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ unsigned char data[4096];
+ isc_buffer_t buffer;
+ dst_key_t *dstkey = NULL;
+
+ isc_buffer_init(&buffer, data, sizeof(data));
+ dns_rdata_fromstruct(&rdata, dnskey->common.rdclass,
+ dns_rdatatype_dnskey, dnskey, &buffer);
+
+ result = dns_dnssec_keyfromrdata(name, &rdata, mctx, &dstkey);
+ if (result == ISC_R_SUCCESS) {
+ *tag = dst_key_id(dstkey);
+ dst_key_free(&dstkey);
+ }
+
+ return (result);
+}
+
+/*
+ * Add key to the security roots.
+ */
+static void
+trust_key(dns_zone_t *zone, dns_name_t *keyname,
+ dns_rdata_dnskey_t *dnskey, isc_mem_t *mctx)
+{
+ isc_result_t result;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ unsigned char data[4096];
+ isc_buffer_t buffer;
+ dns_keytable_t *sr = NULL;
+ dst_key_t *dstkey = NULL;
+
+ /* Convert dnskey to DST key. */
+ isc_buffer_init(&buffer, data, sizeof(data));
+ dns_rdata_fromstruct(&rdata, dnskey->common.rdclass,
+ dns_rdatatype_dnskey, dnskey, &buffer);
+
+ result = dns_view_getsecroots(zone->view, &sr);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ CHECK(dns_dnssec_keyfromrdata(keyname, &rdata, mctx, &dstkey));
+ CHECK(dns_keytable_add(sr, true, &dstkey));
+ dns_keytable_detach(&sr);
+
+ failure:
+ if (dstkey != NULL)
+ dst_key_free(&dstkey);
+ if (sr != NULL)
+ dns_keytable_detach(&sr);
+ return;
+}
+
+/*
+ * Add a null key to the security roots for so that all queries
+ * to the zone will fail.
+ */
+static void
+fail_secure(dns_zone_t *zone, dns_name_t *keyname) {
+ isc_result_t result;
+ dns_keytable_t *sr = NULL;
+
+ result = dns_view_getsecroots(zone->view, &sr);
+ if (result == ISC_R_SUCCESS) {
+ dns_keytable_marksecure(sr, keyname);
+ dns_keytable_detach(&sr);
+ }
+}
+
+/*
+ * Scan a set of KEYDATA records from the key zone. The ones that are
+ * valid (i.e., the add holddown timer has expired) become trusted keys.
+ */
+static void
+load_secroots(dns_zone_t *zone, dns_name_t *name, dns_rdataset_t *rdataset) {
+ isc_result_t result;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_keydata_t keydata;
+ dns_rdata_dnskey_t dnskey;
+ isc_mem_t *mctx = zone->mctx;
+ int trusted = 0, revoked = 0, pending = 0;
+ isc_stdtime_t now;
+ dns_keytable_t *sr = NULL;
+
+ isc_stdtime_get(&now);
+
+ result = dns_view_getsecroots(zone->view, &sr);
+ if (result == ISC_R_SUCCESS) {
+ dns_keytable_delete(sr, name);
+ dns_keytable_detach(&sr);
+ }
+
+ /* Now insert all the accepted trust anchors from this keydata set. */
+ for (result = dns_rdataset_first(rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(rdataset)) {
+ dns_rdata_reset(&rdata);
+ dns_rdataset_current(rdataset, &rdata);
+
+ /* Convert rdata to keydata. */
+ result = dns_rdata_tostruct(&rdata, &keydata, NULL);
+ if (result == ISC_R_UNEXPECTEDEND)
+ continue;
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ /* Set the key refresh timer to force a fast refresh. */
+ set_refreshkeytimer(zone, &keydata, now, true);
+
+ /* If the removal timer is nonzero, this key was revoked. */
+ if (keydata.removehd != 0) {
+ revoked++;
+ continue;
+ }
+
+ /*
+ * If the add timer is still pending, this key is not
+ * trusted yet.
+ */
+ if (now < keydata.addhd) {
+ pending++;
+ continue;
+ }
+
+ /* Convert keydata to dnskey. */
+ dns_keydata_todnskey(&keydata, &dnskey, NULL);
+
+ /* Add to keytables. */
+ trusted++;
+ trust_key(zone, name, &dnskey, mctx);
+ }
+
+ if (trusted == 0 && pending != 0) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ dns_name_format(name, namebuf, sizeof namebuf);
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "No valid trust anchors for '%s'!", namebuf);
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "%d key(s) revoked, %d still pending",
+ revoked, pending);
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "All queries to '%s' will fail", namebuf);
+ fail_secure(zone, name);
+ }
+}
+
+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);
+ 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
+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_result_t
+update_soa_serial(dns_db_t *db, dns_dbversion_t *ver, dns_diff_t *diff,
+ isc_mem_t *mctx, dns_updatemethod_t method) {
+ dns_difftuple_t *deltuple = NULL;
+ dns_difftuple_t *addtuple = NULL;
+ uint32_t serial;
+ isc_result_t result;
+
+ INSIST(method != dns_updatemethod_none);
+
+ 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);
+ serial = dns_update_soaserial(serial, method);
+ 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);
+}
+
+/*
+ * Write all transactions in 'diff' to the zone journal file.
+ */
+static isc_result_t
+zone_journal(dns_zone_t *zone, dns_diff_t *diff, uint32_t *sourceserial,
+ const char *caller)
+{
+ const char me[] = "zone_journal";
+ const char *journalfile;
+ isc_result_t result = ISC_R_SUCCESS;
+ dns_journal_t *journal = NULL;
+ unsigned int mode = DNS_JOURNAL_CREATE|DNS_JOURNAL_WRITE;
+
+ ENTER;
+ journalfile = dns_zone_getjournal(zone);
+ if (journalfile != NULL) {
+ result = dns_journal_open(zone->mctx, journalfile, mode,
+ &journal);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "%s:dns_journal_open -> %s",
+ caller, dns_result_totext(result));
+ return (result);
+ }
+
+ if (sourceserial != NULL)
+ dns_journal_set_sourceserial(journal, *sourceserial);
+
+ result = dns_journal_write_transaction(journal, diff);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "%s:dns_journal_write_transaction -> %s",
+ caller, dns_result_totext(result));
+ }
+ dns_journal_destroy(&journal);
+ }
+
+ return (result);
+}
+
+/*
+ * Create an SOA record for a newly-created zone
+ */
+static isc_result_t
+add_soa(dns_zone_t *zone, dns_db_t *db) {
+ isc_result_t result;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ unsigned char buf[DNS_SOA_BUFFERSIZE];
+ dns_dbversion_t *ver = NULL;
+ dns_diff_t diff;
+
+ dns_zone_log(zone, ISC_LOG_DEBUG(1), "creating SOA");
+
+ dns_diff_init(zone->mctx, &diff);
+ result = dns_db_newversion(db, &ver);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "add_soa:dns_db_newversion -> %s",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ /* Build SOA record */
+ result = dns_soa_buildrdata(&zone->origin, dns_rootname, zone->rdclass,
+ 0, 0, 0, 0, 0, buf, &rdata);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "add_soa:dns_soa_buildrdata -> %s",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ result = update_one_rr(db, ver, &diff, DNS_DIFFOP_ADD,
+ &zone->origin, 0, &rdata);
+
+failure:
+ dns_diff_clear(&diff);
+ if (ver != NULL)
+ dns_db_closeversion(db, &ver, (result == ISC_R_SUCCESS));
+
+ INSIST(ver == NULL);
+
+ return (result);
+}
+
+struct addifmissing_arg {
+ dns_db_t *db;
+ dns_dbversion_t *ver;
+ dns_diff_t *diff;
+ dns_zone_t *zone;
+ bool *changed;
+ isc_result_t result;
+};
+
+static void
+addifmissing(dns_keytable_t *keytable, dns_keynode_t *keynode, void *arg) {
+ dns_db_t *db = ((struct addifmissing_arg *)arg)->db;
+ dns_dbversion_t *ver = ((struct addifmissing_arg *)arg)->ver;
+ dns_diff_t *diff = ((struct addifmissing_arg *)arg)->diff;
+ dns_zone_t *zone = ((struct addifmissing_arg *)arg)->zone;
+ bool *changed = ((struct addifmissing_arg *)arg)->changed;
+ isc_result_t result;
+ dns_keynode_t *dummy = NULL;
+
+ if (((struct addifmissing_arg *)arg)->result != ISC_R_SUCCESS)
+ return;
+
+ if (dns_keynode_managed(keynode)) {
+ dns_fixedname_t fname;
+ dns_name_t *keyname;
+ dst_key_t *key;
+
+ key = dns_keynode_key(keynode);
+ if (key == NULL)
+ return;
+ dns_fixedname_init(&fname);
+
+ keyname = dst_key_name(key);
+ result = dns_db_find(db, keyname, ver,
+ dns_rdatatype_keydata,
+ DNS_DBFIND_NOWILD, 0, NULL,
+ dns_fixedname_name(&fname),
+ NULL, NULL);
+ if (result == ISC_R_SUCCESS)
+ return;
+ dns_keytable_attachkeynode(keytable, keynode, &dummy);
+ result = create_keydata(zone, db, ver, diff, keytable,
+ &dummy, changed);
+ if (result != ISC_R_SUCCESS && result != ISC_R_NOMORE)
+ ((struct addifmissing_arg *)arg)->result = result;
+ }
+};
+
+/*
+ * Synchronize the set of initializing keys found in managed-keys {}
+ * statements with the set of trust anchors found in the managed-keys.bind
+ * zone. If a domain is no longer named in managed-keys, delete all keys
+ * from that domain from the key zone. If a domain is mentioned in in
+ * managed-keys but there are no references to it in the key zone, load
+ * the key zone with the initializing key(s) for that domain.
+ */
+static isc_result_t
+sync_keyzone(dns_zone_t *zone, dns_db_t *db) {
+ isc_result_t result = ISC_R_SUCCESS;
+ bool changed = false;
+ bool commit = false;
+ dns_keynode_t *keynode = NULL;
+ dns_view_t *view = zone->view;
+ dns_keytable_t *sr = NULL;
+ dns_dbversion_t *ver = NULL;
+ dns_diff_t diff;
+ dns_rriterator_t rrit;
+ struct addifmissing_arg arg;
+
+ dns_zone_log(zone, ISC_LOG_DEBUG(1), "synchronizing trusted keys");
+
+ dns_diff_init(zone->mctx, &diff);
+
+ CHECK(dns_view_getsecroots(view, &sr));
+
+ result = dns_db_newversion(db, &ver);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "sync_keyzone:dns_db_newversion -> %s",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ /*
+ * Walk the zone DB. If we find any keys whose names are no longer
+ * in managed-keys (or *are* in trusted-keys, meaning they are
+ * permanent and not RFC5011-maintained), delete them from the
+ * zone. Otherwise call load_secroots(), which loads keys into
+ * secroots as appropriate.
+ */
+ dns_rriterator_init(&rrit, db, ver, 0);
+ for (result = dns_rriterator_first(&rrit);
+ result == ISC_R_SUCCESS;
+ result = dns_rriterator_nextrrset(&rrit)) {
+ dns_rdataset_t *rdataset = NULL;
+ dns_name_t *rrname = NULL;
+ uint32_t ttl;
+
+ dns_rriterator_current(&rrit, &rrname, &ttl,
+ &rdataset, NULL);
+ if (!dns_rdataset_isassociated(rdataset)) {
+ dns_rriterator_destroy(&rrit);
+ goto failure;
+ }
+
+ if (rdataset->type != dns_rdatatype_keydata)
+ continue;
+
+ result = dns_keytable_find(sr, rrname, &keynode);
+ if ((result != ISC_R_SUCCESS &&
+ result != DNS_R_PARTIALMATCH) ||
+ dns_keynode_managed(keynode) == false)
+ {
+ CHECK(delete_keydata(db, ver, &diff,
+ rrname, rdataset));
+ changed = true;
+ } else {
+ load_secroots(zone, rrname, rdataset);
+ }
+
+ if (keynode != NULL)
+ dns_keytable_detachkeynode(sr, &keynode);
+ }
+ dns_rriterator_destroy(&rrit);
+
+ /*
+ * Now walk secroots to find any managed keys that aren't
+ * in the zone. If we find any, we add them to the zone.
+ */
+ arg.db = db;
+ arg.ver = ver;
+ arg.result = ISC_R_SUCCESS;
+ arg.diff = &diff;
+ arg.zone = zone;
+ arg.changed = &changed;
+ dns_keytable_forall(sr, addifmissing, &arg);
+ result = arg.result;
+ if (changed) {
+ /* Write changes to journal file. */
+ CHECK(update_soa_serial(db, ver, &diff, zone->mctx,
+ zone->updatemethod));
+ CHECK(zone_journal(zone, &diff, NULL, "sync_keyzone"));
+
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED);
+ zone_needdump(zone, 30);
+ commit = true;
+ }
+
+ failure:
+ if (result != ISC_R_SUCCESS &&
+ !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADED)) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "unable to synchronize managed keys: %s",
+ dns_result_totext(result));
+ isc_time_settoepoch(&zone->refreshkeytime);
+ }
+ if (keynode != NULL)
+ dns_keytable_detachkeynode(sr, &keynode);
+ if (sr != NULL)
+ dns_keytable_detach(&sr);
+ if (ver != NULL)
+ dns_db_closeversion(db, &ver, commit);
+ dns_diff_clear(&diff);
+
+ INSIST(ver == NULL);
+
+ return (result);
+}
+
+isc_result_t
+dns_zone_synckeyzone(dns_zone_t *zone) {
+ isc_result_t result;
+ dns_db_t *db = NULL;
+
+ if (zone->type != dns_zone_key)
+ return (DNS_R_BADZONE);
+
+ CHECK(dns_zone_getdb(zone, &db));
+
+ LOCK_ZONE(zone);
+ result = sync_keyzone(zone, db);
+ UNLOCK_ZONE(zone);
+
+ failure:
+ if (db != NULL)
+ dns_db_detach(&db);
+ return (result);
+}
+
+static void
+maybe_send_secure(dns_zone_t *zone) {
+ isc_result_t result;
+
+ /*
+ * We've finished loading, or else failed to load, an inline-signing
+ * 'secure' zone. We now need information about the status of the
+ * 'raw' zone. If we failed to load, then we need it to send a
+ * copy of its database; if we succeeded, we need it to send its
+ * serial number so that we can sync with it. If it has not yet
+ * loaded, we set a flag so that it will send the necessary
+ * information when it has finished loading.
+ */
+ if (zone->raw->db != NULL) {
+ if (zone->db != NULL) {
+ uint32_t serial;
+ unsigned int soacount;
+
+ result = zone_get_from_db(zone->raw, zone->raw->db,
+ NULL, &soacount, &serial, NULL,
+ NULL, NULL, NULL, NULL);
+ if (result == ISC_R_SUCCESS && soacount > 0U)
+ zone_send_secureserial(zone->raw, serial);
+ } else
+ zone_send_securedb(zone->raw, zone->raw->db);
+
+ } else
+ DNS_ZONE_SETFLAG(zone->raw, DNS_ZONEFLG_SENDSECURE);
+}
+
+static bool
+zone_unchanged(dns_db_t *db1, dns_db_t *db2, isc_mem_t *mctx) {
+ isc_result_t result;
+ bool answer = false;
+ dns_diff_t diff;
+
+ dns_diff_init(mctx, &diff);
+ result = dns_db_diffx(&diff, db1, NULL, db2, NULL, NULL);
+ if (result == ISC_R_SUCCESS && ISC_LIST_EMPTY(diff.tuples))
+ answer = true;
+ dns_diff_clear(&diff);
+ return (answer);
+}
+
+/*
+ * The zone is presumed to be locked.
+ * If this is a inline_raw zone the secure version is also locked.
+ */
+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;
+ uint32_t serial, oldserial, refresh, retry, expire, minimum;
+ isc_time_t now;
+ bool needdump = false;
+ bool hasinclude = DNS_ZONE_FLAG(zone, DNS_ZONEFLG_HASINCLUDE);
+ bool nomaster = false;
+ unsigned int options;
+ dns_include_t *inc;
+
+ INSIST(LOCKED_ZONE(zone));
+ if (inline_raw(zone))
+ INSIST(LOCKED_ZONE(zone->secure));
+
+ 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 ||
+ (zone->type == dns_zone_redirect &&
+ zone->masters == NULL)) {
+ 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 if (zone->type == dns_zone_master &&
+ inline_secure(zone) && result == ISC_R_FILENOTFOUND)
+ {
+ dns_zone_log(zone, ISC_LOG_DEBUG(1),
+ "no master file, requesting db");
+ maybe_send_secure(zone);
+ } else {
+ int level = ISC_LOG_ERROR;
+ if (zone->type == dns_zone_key &&
+ result == ISC_R_FILENOTFOUND)
+ level = ISC_LOG_DEBUG(1);
+ dns_zone_log(zone, level,
+ "loading from master file %s failed: %s",
+ zone->masterfile,
+ dns_result_totext(result));
+ nomaster = true;
+ }
+
+ if (zone->type != dns_zone_key)
+ 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);
+
+ /*
+ * If there's no master file for a key zone, then the zone is new:
+ * create an SOA record. (We do this now, instead of later, so that
+ * if there happens to be a journal file, we can roll forward from
+ * a sane starting point.)
+ */
+ if (nomaster && zone->type == dns_zone_key) {
+ result = add_soa(zone, db);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+
+ /*
+ * 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 && (inline_secure(zone) ||
+ (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 = true;
+ }
+
+ /*
+ * 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 && zone->type != dns_zone_key) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "could not find NS and/or SOA records");
+ }
+
+ /*
+ * Check to make sure the journal is up to date, and remove the
+ * journal file if it isn't, as we wouldn't be able to apply
+ * updates otherwise.
+ */
+ if (zone->journal != NULL && dns_zone_isdynamic(zone, true) &&
+ ! DNS_ZONE_OPTION(zone, DNS_ZONEOPT_IXFRFROMDIFFS)) {
+ uint32_t jserial;
+ dns_journal_t *journal = NULL;
+
+ result = dns_journal_open(zone->mctx, zone->journal,
+ DNS_JOURNAL_READ, &journal);
+ if (result == ISC_R_SUCCESS) {
+ jserial = dns_journal_last_serial(journal);
+ dns_journal_destroy(&journal);
+ } else {
+ jserial = serial;
+ result = ISC_R_SUCCESS;
+ }
+
+ if (jserial != serial) {
+ dns_zone_log(zone, ISC_LOG_INFO,
+ "journal file is out of date: "
+ "removing journal file");
+ if (remove(zone->journal) < 0 && errno != ENOENT) {
+ char strbuf[ISC_STRERRORSIZE];
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ isc_log_write(dns_lctx,
+ DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_ZONE,
+ ISC_LOG_WARNING,
+ "unable to remove journal "
+ "'%s': '%s'",
+ zone->journal, strbuf);
+ }
+ }
+ }
+
+ dns_zone_log(zone, ISC_LOG_DEBUG(1), "loaded; checking validity");
+
+ /*
+ * Master / Slave / Stub zones require both NS and SOA records at
+ * the top of the zone.
+ */
+
+ switch (zone->type) {
+ case dns_zone_dlz:
+ case dns_zone_master:
+ case dns_zone_slave:
+ case dns_zone_stub:
+ case dns_zone_redirect:
+ 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 &&
+ zone->type != dns_zone_redirect) {
+ 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->type == dns_zone_master &&
+ DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKDUPRR) &&
+ !zone_check_dup(zone, db)) {
+ result = DNS_R_BADZONE;
+ goto cleanup;
+ }
+
+ if (zone->db != NULL) {
+ unsigned int oldsoacount;
+
+ /*
+ * This is checked in zone_replacedb() for slave zones
+ * as they don't reload from disk.
+ */
+ result = zone_get_from_db(zone, zone->db, NULL,
+ &oldsoacount, &oldserial,
+ NULL, NULL, NULL, NULL,
+ NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ RUNTIME_CHECK(soacount > 0U);
+ if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_IXFRFROMDIFFS) &&
+ !isc_serial_gt(serial, oldserial)) {
+ uint32_t serialmin, serialmax;
+
+ INSIST(zone->type == dns_zone_master);
+ INSIST(zone->raw == NULL);
+
+ if (serial == oldserial &&
+ zone_unchanged(zone->db, db, zone->mctx)) {
+ dns_zone_log(zone, ISC_LOG_INFO,
+ "ixfr-from-differences: "
+ "unchanged");
+ goto done;
+ }
+
+ serialmin = (oldserial + 1) & 0xffffffffU;
+ serialmax = (oldserial + 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, oldserial))
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone serial (%u/%u) has gone "
+ "backwards", serial, oldserial);
+ else if (serial == oldserial && !hasinclude &&
+ strcmp(zone->db_argv[0], "_builtin") != 0)
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone serial (%u) unchanged. "
+ "zone may fail to transfer "
+ "to slaves.", serial);
+ }
+
+ 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->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 ||
+ (zone->type == dns_zone_redirect &&
+ zone->masters != NULL)) {
+ isc_time_t t;
+ 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;
+
+ case dns_zone_key:
+ result = sync_keyzone(zone, db);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ 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);
+
+ /*
+ * Schedule DNSSEC key refresh.
+ */
+ if (zone->type == dns_zone_master &&
+ DNS_ZONEKEY_OPTION(zone, DNS_ZONEKEY_MAINTAIN))
+ zone->refreshkeytime = now;
+
+#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, false);
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_write);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ } else {
+ result = dns_db_rpz_ready(db);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ zone_attachdb(zone, db);
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_write);
+ DNS_ZONE_SETFLAG(zone,
+ DNS_ZONEFLG_LOADED|
+ DNS_ZONEFLG_NEEDSTARTUPNOTIFY);
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_SENDSECURE) &&
+ inline_raw(zone))
+ {
+ if (zone->secure->db == NULL)
+ zone_send_securedb(zone, db);
+ else
+ zone_send_secureserial(zone, serial);
+ }
+ }
+
+ /*
+ * Finished loading inline-signing zone; need to get status
+ * from the raw side now.
+ */
+ if (zone->type == dns_zone_master && inline_secure(zone))
+ maybe_send_secure(zone);
+
+
+ result = ISC_R_SUCCESS;
+
+ if (needdump) {
+ if (zone->type == dns_zone_key)
+ zone_needdump(zone, 30);
+ else
+ 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);
+ }
+
+ if (zone->type == dns_zone_master &&
+ !DNS_ZONEKEY_OPTION(zone, DNS_ZONEKEY_NORESIGN) &&
+ dns_zone_isdynamic(zone, false) &&
+ dns_db_issecure(db)) {
+ dns_name_t *name;
+ dns_fixedname_t fixed;
+ dns_rdataset_t next;
+
+ dns_rdataset_init(&next);
+ name = dns_fixedname_initname(&fixed);
+
+ result = dns_db_getsigningtime(db, &next, name);
+ if (result == ISC_R_SUCCESS) {
+ isc_stdtime_t timenow;
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char typebuf[DNS_RDATATYPE_FORMATSIZE];
+
+ isc_stdtime_get(&timenow);
+ dns_name_format(name, namebuf, sizeof(namebuf));
+ dns_rdatatype_format(next.covers,
+ typebuf, sizeof(typebuf));
+ dns_zone_log(zone, ISC_LOG_DEBUG(3),
+ "next resign: %s/%s in %d seconds",
+ namebuf, typebuf,
+ next.resign - timenow -
+ zone->sigresigninginterval);
+ dns_rdataset_disassociate(&next);
+ } else
+ dns_zone_log(zone, ISC_LOG_WARNING,
+ "signed dynamic zone has no "
+ "resign event scheduled");
+ }
+
+ zone_settimer(zone, &now);
+ }
+
+ /*
+ * Clear old include list.
+ */
+ for (inc = ISC_LIST_HEAD(zone->includes);
+ inc != NULL;
+ inc = ISC_LIST_HEAD(zone->includes)) {
+ ISC_LIST_UNLINK(zone->includes, inc, link);
+ isc_mem_free(zone->mctx, inc->name);
+ isc_mem_put(zone->mctx, inc, sizeof(*inc));
+ }
+ zone->nincludes = 0;
+
+ /*
+ * Transfer new include list.
+ */
+ for (inc = ISC_LIST_HEAD(zone->newincludes);
+ inc != NULL;
+ inc = ISC_LIST_HEAD(zone->newincludes)) {
+ ISC_LIST_UNLINK(zone->newincludes, inc, link);
+ ISC_LIST_APPEND(zone->includes, inc, link);
+ zone->nincludes++;
+ }
+
+ if (! dns_db_ispersistent(db))
+ dns_zone_log(zone, ISC_LOG_INFO, "loaded serial %u%s", serial,
+ dns_db_issecure(db) ? " (DNSSEC signed)" : "");
+
+ zone->loadtime = loadtime;
+ goto done;
+
+ cleanup:
+ for (inc = ISC_LIST_HEAD(zone->newincludes);
+ inc != NULL;
+ inc = ISC_LIST_HEAD(zone->newincludes)) {
+ ISC_LIST_UNLINK(zone->newincludes, inc, link);
+ isc_mem_free(zone->mctx, inc->name);
+ isc_mem_put(zone->mctx, inc, sizeof(*inc));
+ }
+ if (zone->type == dns_zone_slave ||
+ zone->type == dns_zone_stub ||
+ zone->type == dns_zone_key ||
+ (zone->type == dns_zone_redirect && zone->masters != NULL)) {
+ if (result != ISC_R_NOMEMORY) {
+ 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;
+ } else if (zone->type == dns_zone_master ||
+ zone->type == dns_zone_redirect) {
+ if (!(inline_secure(zone) && result == ISC_R_FILENOTFOUND))
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "not loaded due to errors.");
+ else if (zone->type == dns_zone_master)
+ result = ISC_R_SUCCESS;
+ }
+
+ done:
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_LOADPENDING);
+ /*
+ * If this is an inline-signed zone and we were called for the raw
+ * zone, we need to clear DNS_ZONEFLG_LOADPENDING for the secure zone
+ * as well, but only if this is a reload, not an initial zone load: in
+ * the former case, zone_postload() will not be run for the secure
+ * zone; in the latter case, it will be. Check which case we are
+ * dealing with by consulting the DNS_ZONEFLG_LOADED flag for the
+ * secure zone: if it is set, this must be a reload.
+ */
+ if (inline_raw(zone) &&
+ DNS_ZONE_FLAG(zone->secure, DNS_ZONEFLG_LOADED))
+ {
+ DNS_ZONE_CLRFLAG(zone->secure, DNS_ZONEFLG_LOADPENDING);
+ }
+
+ return (result);
+}
+
+static bool
+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 (true);
+ }
+ return (false);
+}
+
+static bool
+zone_check_ns(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version,
+ dns_name_t *name, bool logit)
+{
+ 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 (true);
+
+ if (zone->type == dns_zone_master)
+ level = ISC_LOG_ERROR;
+ else
+ level = ISC_LOG_WARNING;
+
+ foundname = dns_fixedname_initname(&fixed);
+
+ result = dns_db_find(db, name, version, dns_rdatatype_a,
+ 0, 0, NULL, foundname, NULL, NULL);
+ if (result == ISC_R_SUCCESS)
+ return (true);
+
+ if (result == DNS_R_NXRRSET) {
+ result = dns_db_find(db, name, version, dns_rdatatype_aaaa,
+ 0, 0, NULL, foundname, NULL, NULL);
+ if (result == ISC_R_SUCCESS)
+ return (true);
+ }
+
+ if (result == DNS_R_NXRRSET || result == DNS_R_NXDOMAIN ||
+ result == DNS_R_EMPTYNAME) {
+ if (logit) {
+ dns_name_format(name, namebuf, sizeof namebuf);
+ dns_zone_log(zone, level, "NS '%s' has no address "
+ "records (A or AAAA)", namebuf);
+ }
+ return (false);
+ }
+
+ if (result == DNS_R_CNAME) {
+ if (logit) {
+ dns_name_format(name, namebuf, sizeof namebuf);
+ dns_zone_log(zone, level, "NS '%s' is a CNAME "
+ "(illegal)", namebuf);
+ }
+ return (false);
+ }
+
+ if (result == DNS_R_DNAME) {
+ if (logit) {
+ dns_name_format(name, namebuf, sizeof namebuf);
+ dns_name_format(foundname, altbuf, sizeof altbuf);
+ dns_zone_log(zone, level, "NS '%s' is below a DNAME "
+ "'%s' (illegal)", namebuf, altbuf);
+ }
+ return (false);
+ }
+
+ return (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, bool logit)
+{
+ 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) {
+ INSIST(!dns_rdataset_isassociated(&rdataset));
+ goto success;
+ }
+ if (result != ISC_R_SUCCESS) {
+ INSIST(!dns_rdataset_isassociated(&rdataset));
+ 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, version, &ns.name, logit))
+ 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,
+ uint32_t *serial, uint32_t *refresh,
+ uint32_t *retry, uint32_t *expire,
+ 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) {
+ INSIST(!dns_rdataset_isassociated(&rdataset));
+ 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) {
+ INSIST(!dns_rdataset_isassociated(&rdataset));
+ 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;
+ } else {
+ 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;
+
+ 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, uint32_t *serial,
+ uint32_t *refresh, uint32_t *retry,
+ uint32_t *expire, uint32_t *minimum,
+ unsigned int *errors)
+{
+ isc_result_t result;
+ isc_result_t answer = ISC_R_SUCCESS;
+ dns_dbversion_t *version = NULL;
+ dns_dbnode_t *node;
+
+ REQUIRE(db != NULL);
+ REQUIRE(zone != NULL);
+
+ dns_db_currentversion(db, &version);
+
+ if (nscount != NULL)
+ *nscount = 0;
+ 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 (errors != NULL)
+ *errors = 0;
+
+ node = NULL;
+ result = dns_db_findnode(db, &zone->origin, 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, true);
+ 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, 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;
+ dns_zone_t *raw = NULL;
+ dns_zone_t *secure = NULL;
+ unsigned int refs;
+ bool free_now = false;
+
+ REQUIRE(zonep != NULL && DNS_ZONE_VALID(*zonep));
+
+ zone = *zonep;
+
+ isc_refcount_decrement(&zone->erefs, &refs);
+
+ if (refs == 0) {
+ LOCK_ZONE(zone);
+ INSIST(zone != zone->raw);
+ /*
+ * 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 = true;
+ raw = zone->raw;
+ zone->raw = NULL;
+ secure = zone->secure;
+ zone->secure = NULL;
+ }
+ UNLOCK_ZONE(zone);
+ }
+ *zonep = NULL;
+ if (free_now) {
+ if (raw != NULL)
+ dns_zone_detach(&raw);
+ if (secure != NULL)
+ dns_zone_idetach(&secure);
+ 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;
+ bool 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, bool 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,
+ bool value)
+{
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ if (value)
+ zone->options |= option;
+ else
+ zone->options &= ~option;
+ UNLOCK_ZONE(zone);
+}
+
+void
+dns_zone_setoption2(dns_zone_t *zone, unsigned int option,
+ bool value)
+{
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ if (value)
+ zone->options2 |= option;
+ else
+ zone->options2 &= ~option;
+ UNLOCK_ZONE(zone);
+}
+
+unsigned int
+dns_zone_getoptions(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return (zone->options);
+}
+
+unsigned int
+dns_zone_getoptions2(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return (zone->options2);
+}
+
+void
+dns_zone_setkeyopt(dns_zone_t *zone, unsigned int keyopt, bool value)
+{
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ if (value)
+ zone->keyopts |= keyopt;
+ else
+ zone->keyopts &= ~keyopt;
+ UNLOCK_ZONE(zone);
+}
+
+unsigned int
+dns_zone_getkeyopts(dns_zone_t *zone) {
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return (zone->keyopts);
+}
+
+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_setxfrsource4dscp(dns_zone_t *zone, isc_dscp_t dscp) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ zone->xfrsource4dscp = dscp;
+ UNLOCK_ZONE(zone);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_dscp_t
+dns_zone_getxfrsource4dscp(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ return (zone->xfrsource4dscp);
+}
+
+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_dscp_t
+dns_zone_getxfrsource6dscp(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ return (zone->xfrsource6dscp);
+}
+
+isc_result_t
+dns_zone_setxfrsource6dscp(dns_zone_t *zone, isc_dscp_t dscp) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ zone->xfrsource6dscp = dscp;
+ UNLOCK_ZONE(zone);
+
+ return (ISC_R_SUCCESS);
+}
+
+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_setaltxfrsource4dscp(dns_zone_t *zone, isc_dscp_t dscp) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ zone->altxfrsource4dscp = dscp;
+ UNLOCK_ZONE(zone);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_dscp_t
+dns_zone_getaltxfrsource4dscp(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ return (zone->altxfrsource4dscp);
+}
+
+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_setaltxfrsource6dscp(dns_zone_t *zone, isc_dscp_t dscp) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ zone->altxfrsource6dscp = dscp;
+ UNLOCK_ZONE(zone);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_dscp_t
+dns_zone_getaltxfrsource6dscp(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ return (zone->altxfrsource6dscp);
+}
+
+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_setnotifysrc4dscp(dns_zone_t *zone, isc_dscp_t dscp) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ zone->notifysrc4dscp = dscp;
+ UNLOCK_ZONE(zone);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_dscp_t
+dns_zone_getnotifysrc4dscp(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ return (zone->notifysrc4dscp);
+}
+
+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);
+}
+
+static bool
+same_addrs(isc_sockaddr_t const *oldlist, isc_sockaddr_t const *newlist,
+ uint32_t count)
+{
+ unsigned int i;
+
+ for (i = 0; i < count; i++)
+ if (!isc_sockaddr_equal(&oldlist[i], &newlist[i]))
+ return (false);
+ return (true);
+}
+
+static bool
+same_keynames(dns_name_t * const *oldlist, dns_name_t * const *newlist,
+ uint32_t count)
+{
+ unsigned int i;
+
+ if (oldlist == NULL && newlist == NULL)
+ return (true);
+ if (oldlist == NULL || newlist == NULL)
+ return (false);
+
+ for (i = 0; i < count; i++) {
+ if (oldlist[i] == NULL && newlist[i] == NULL)
+ continue;
+ if (oldlist[i] == NULL || newlist[i] == NULL ||
+ !dns_name_equal(oldlist[i], newlist[i]))
+ return (false);
+ }
+ return (true);
+}
+
+static void
+clear_addresskeylist(isc_sockaddr_t **addrsp, isc_dscp_t **dscpsp,
+ dns_name_t ***keynamesp, unsigned int *countp,
+ isc_mem_t *mctx)
+{
+ unsigned int count;
+ isc_sockaddr_t *addrs;
+ isc_dscp_t *dscps;
+ dns_name_t **keynames;
+
+ REQUIRE(countp != NULL && addrsp != NULL && dscpsp != NULL &&
+ keynamesp != NULL);
+
+ count = *countp;
+ *countp = 0;
+ addrs = *addrsp;
+ *addrsp = NULL;
+ dscps = *dscpsp;
+ *dscpsp = NULL;
+ keynames = *keynamesp;
+ *keynamesp = NULL;
+
+ if (addrs != NULL)
+ isc_mem_put(mctx, addrs, count * sizeof(isc_sockaddr_t));
+
+ if (dscps != NULL)
+ isc_mem_put(mctx, dscps, count * sizeof(isc_dscp_t));
+
+ if (keynames != NULL) {
+ unsigned int i;
+ for (i = 0; i < count; i++) {
+ if (keynames[i] != NULL) {
+ dns_name_free(keynames[i], mctx);
+ isc_mem_put(mctx, keynames[i],
+ sizeof(dns_name_t));
+ keynames[i] = NULL;
+ }
+ }
+ isc_mem_put(mctx, keynames, count * sizeof(dns_name_t *));
+ }
+}
+
+static isc_result_t
+set_addrkeylist(unsigned int count,
+ const isc_sockaddr_t *addrs, isc_sockaddr_t **newaddrsp,
+ const isc_dscp_t *dscp, isc_dscp_t **newdscpp,
+ dns_name_t **names, dns_name_t ***newnamesp,
+ isc_mem_t *mctx)
+{
+ isc_result_t result;
+ isc_sockaddr_t *newaddrs = NULL;
+ isc_dscp_t *newdscp = NULL;
+ dns_name_t **newnames = NULL;
+ unsigned int i;
+
+ REQUIRE(newaddrsp != NULL && *newaddrsp == NULL);
+ REQUIRE(newdscpp != NULL && *newdscpp == NULL);
+ REQUIRE(newnamesp != NULL && *newnamesp == NULL);
+
+ newaddrs = isc_mem_get(mctx, count * sizeof(*newaddrs));
+ if (newaddrs == NULL)
+ return (ISC_R_NOMEMORY);
+ memmove(newaddrs, addrs, count * sizeof(*newaddrs));
+
+ if (dscp != NULL) {
+ newdscp = isc_mem_get(mctx, count * sizeof(*newdscp));
+ if (newdscp == NULL) {
+ isc_mem_put(mctx, newaddrs, count * sizeof(*newaddrs));
+ return (ISC_R_NOMEMORY);
+ }
+ memmove(newdscp, dscp, count * sizeof(*newdscp));
+ } else
+ newdscp = NULL;
+
+ if (names != NULL) {
+ newnames = isc_mem_get(mctx, count * sizeof(*newnames));
+ if (newnames == NULL) {
+ if (newdscp != NULL)
+ isc_mem_put(mctx, newdscp,
+ count * sizeof(*newdscp));
+ isc_mem_put(mctx, newaddrs, count * sizeof(*newaddrs));
+ return (ISC_R_NOMEMORY);
+ }
+ for (i = 0; i < count; i++)
+ newnames[i] = NULL;
+ for (i = 0; i < count; i++) {
+ if (names[i] != NULL) {
+ newnames[i] = isc_mem_get(mctx,
+ sizeof(dns_name_t));
+ if (newnames[i] == NULL)
+ goto allocfail;
+ dns_name_init(newnames[i], NULL);
+ result = dns_name_dup(names[i], mctx,
+ newnames[i]);
+ if (result != ISC_R_SUCCESS) {
+ allocfail:
+ for (i = 0; i < count; i++)
+ if (newnames[i] != NULL)
+ dns_name_free(
+ newnames[i],
+ mctx);
+ isc_mem_put(mctx, newaddrs,
+ count * sizeof(*newaddrs));
+ isc_mem_put(mctx, newdscp,
+ count * sizeof(*newdscp));
+ isc_mem_put(mctx, newnames,
+ count * sizeof(*newnames));
+ return (ISC_R_NOMEMORY);
+ }
+ }
+ }
+ } else
+ newnames = NULL;
+
+ *newdscpp = newdscp;
+ *newaddrsp = newaddrs;
+ *newnamesp = newnames;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_zone_setnotifysrc6dscp(dns_zone_t *zone, isc_dscp_t dscp) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ zone->notifysrc6dscp = dscp;
+ UNLOCK_ZONE(zone);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_dscp_t
+dns_zone_getnotifysrc6dscp(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ return (zone->notifysrc6dscp);
+}
+
+isc_result_t
+dns_zone_setalsonotify(dns_zone_t *zone, const isc_sockaddr_t *notify,
+ uint32_t count)
+{
+ return (dns_zone_setalsonotifydscpkeys(zone, notify, NULL, NULL,
+ count));
+}
+
+isc_result_t
+dns_zone_setalsonotifywithkeys(dns_zone_t *zone, const isc_sockaddr_t *notify,
+ dns_name_t **keynames, uint32_t count)
+{
+ return (dns_zone_setalsonotifydscpkeys(zone, notify, NULL, keynames,
+ count));
+}
+
+isc_result_t
+dns_zone_setalsonotifydscpkeys(dns_zone_t *zone, const isc_sockaddr_t *notify,
+ const isc_dscp_t *dscps, dns_name_t **keynames,
+ uint32_t count)
+{
+ isc_result_t result;
+ isc_sockaddr_t *newaddrs = NULL;
+ isc_dscp_t *newdscps = NULL;
+ dns_name_t **newnames = NULL;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(count == 0 || notify != NULL);
+ if (keynames != NULL)
+ REQUIRE(count != 0);
+
+ LOCK_ZONE(zone);
+
+ if (count == zone->notifycnt &&
+ same_addrs(zone->notify, notify, count) &&
+ same_keynames(zone->notifykeynames, keynames, count))
+ goto unlock;
+
+ clear_addresskeylist(&zone->notify, &zone->notifydscp,
+ &zone->notifykeynames, &zone->notifycnt,
+ zone->mctx);
+
+ if (count == 0)
+ goto unlock;
+
+ /*
+ * Set up the notify and notifykey lists
+ */
+ result = set_addrkeylist(count, notify, &newaddrs, dscps, &newdscps,
+ keynames, &newnames, zone->mctx);
+ if (result != ISC_R_SUCCESS)
+ goto unlock;
+
+ /*
+ * Everything is ok so attach to the zone.
+ */
+ zone->notify = newaddrs;
+ zone->notifydscp = newdscps;
+ zone->notifykeynames = newnames;
+ zone->notifycnt = count;
+ unlock:
+ UNLOCK_ZONE(zone);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_zone_setmasters(dns_zone_t *zone, const isc_sockaddr_t *masters,
+ uint32_t count)
+{
+ isc_result_t result;
+
+ result = dns_zone_setmasterswithkeys(zone, masters, NULL, count);
+ return (result);
+}
+
+isc_result_t
+dns_zone_setmasterswithkeys(dns_zone_t *zone,
+ const isc_sockaddr_t *masters,
+ dns_name_t **keynames,
+ uint32_t count)
+{
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_sockaddr_t *newaddrs = NULL;
+ isc_dscp_t *newdscps = NULL;
+ dns_name_t **newnames = NULL;
+ bool *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_addrs(zone->masters, masters, count) ||
+ !same_keynames(zone->masterkeynames, keynames, count)) {
+ if (zone->request != NULL)
+ dns_request_cancel(zone->request);
+ } else
+ goto unlock;
+
+ /*
+ * This needs to happen before clear_addresskeylist() sets
+ * zone->masterscnt to 0:
+ */
+ if (zone->mastersok != NULL) {
+ isc_mem_put(zone->mctx, zone->mastersok,
+ zone->masterscnt * sizeof(bool));
+ zone->mastersok = NULL;
+ }
+ clear_addresskeylist(&zone->masters, &zone->masterdscps,
+ &zone->masterkeynames, &zone->masterscnt,
+ zone->mctx);
+ /*
+ * 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;
+
+ /*
+ * mastersok must contain count elements
+ */
+ newok = isc_mem_get(zone->mctx, count * sizeof(*newok));
+ if (newok == NULL) {
+ result = ISC_R_NOMEMORY;
+ isc_mem_put(zone->mctx, newaddrs, count * sizeof(*newaddrs));
+ goto unlock;
+ };
+ for (i = 0; i < count; i++)
+ newok[i] = false;
+
+ /*
+ * Now set up the masters and masterkey lists
+ */
+ result = set_addrkeylist(count, masters, &newaddrs, NULL, &newdscps,
+ keynames, &newnames, zone->mctx);
+ INSIST(newdscps == NULL);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(zone->mctx, newok, count * sizeof(*newok));
+ goto unlock;
+ }
+
+ /*
+ * Everything is ok so attach to the zone.
+ */
+ zone->curmaster = 0;
+ zone->mastersok = newok;
+ zone->masters = newaddrs;
+ zone->masterdscps = newdscps;
+ zone->masterkeynames = newnames;
+ 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);
+}
+
+void
+dns_zone_setdb(dns_zone_t *zone, dns_db_t *db) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(zone->type == dns_zone_staticstub);
+
+ ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_write);
+ REQUIRE(zone->db == NULL);
+ dns_db_attach(db, &zone->db);
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_write);
+}
+
+/*
+ * 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 bool
+was_dumping(dns_zone_t *zone) {
+ bool 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);
+}
+
+/*%
+ * Find up to 'maxkeys' DNSSEC keys used for signing version 'ver' of database
+ * 'db' for zone 'zone' in its key directory, then load these keys into 'keys'.
+ * Only load the public part of a given key if it is not active at timestamp
+ * 'now'. Store the number of keys found in 'nkeys'.
+ */
+isc_result_t
+dns__zone_findkeys(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver,
+ isc_stdtime_t now, 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), false, &node));
+ memset(keys, 0, sizeof(*keys) * maxkeys);
+ result = dns_dnssec_findzonekeys3(db, ver, node, dns_db_origin(db),
+ directory, now, 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__zonediff_t *zonediff,
+ 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, zonediff->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, zonediff->diff, DNS_DIFFOP_ADDRESIGN,
+ name, ttl, rdata);
+ zonediff->offline = true;
+ return (result);
+}
+
+static void
+set_key_expiry_warning(dns_zone_t *zone, isc_stdtime_t when, isc_stdtime_t now)
+{
+ unsigned int delta;
+ char timebuf[80];
+
+ 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) {
+ isc_time_t t;
+ isc_time_set(&t, when, 0);
+ isc_time_formattimestamp(&t, timebuf, 80);
+ dns_zone_log(zone, ISC_LOG_WARNING,
+ "DNSKEY RRSIG(s) will expire within 7 days: %s",
+ timebuf);
+ 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 {
+ isc_time_set(&zone->keywarntime, when - 7 * 24 * 3600, 0);
+ isc_time_formattimestamp(&zone->keywarntime, timebuf, 80);
+ dns_zone_log(zone, ISC_LOG_NOTICE,
+ "setting keywarntime to %s", timebuf);
+ }
+}
+
+/*
+ * Helper function to del_sigs(). We don't want to delete RRSIGs that
+ * have no new key.
+ */
+static bool
+delsig_ok(dns_rdata_rrsig_t *rrsig_ptr, dst_key_t **keys, unsigned int nkeys,
+ bool *warn)
+{
+ unsigned int i = 0;
+ bool have_ksk = false, have_zsk = false;
+ bool have_pksk = false, have_pzsk = false;
+
+ for (i = 0; i < nkeys; i++) {
+ if (rrsig_ptr->algorithm != dst_key_alg(keys[i]))
+ continue;
+ if (dst_key_isprivate(keys[i])) {
+ if (KSK(keys[i]))
+ have_ksk = have_pksk = true;
+ else
+ have_zsk = have_pzsk = true;
+ } else {
+ if (KSK(keys[i]))
+ have_ksk = true;
+ else
+ have_zsk = true;
+ }
+ }
+
+ if (have_zsk && have_ksk && !have_pzsk)
+ *warn = true;
+
+ /*
+ * It's okay to delete a signature if there is an active key
+ * with the same algorithm to replace it.
+ */
+ if (have_pksk || have_pzsk)
+ return (true);
+
+ /*
+ * Failing that, it is *not* okay to delete a signature
+ * if the associated public key is still in the DNSKEY RRset
+ */
+ for (i = 0; i < nkeys; i++) {
+ if ((rrsig_ptr->algorithm == dst_key_alg(keys[i])) &&
+ (rrsig_ptr->keyid == dst_key_id(keys[i])))
+ return (false);
+ }
+
+ /*
+ * But if the key is gone, then go ahead.
+ */
+ return (true);
+}
+
+/*
+ * 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__zonediff_t *zonediff, dst_key_t **keys,
+ unsigned int nkeys, isc_stdtime_t now, bool incremental)
+{
+ isc_result_t result;
+ dns_dbnode_t *node = NULL;
+ dns_rdataset_t rdataset;
+ unsigned int i;
+ dns_rdata_rrsig_t rrsig;
+ bool found;
+ int64_t timewarn = 0, timemaybe = 0;
+
+ dns_rdataset_init(&rdataset);
+
+ if (type == dns_rdatatype_nsec3)
+ result = dns_db_findnsec3node(db, name, false, &node);
+ else
+ result = dns_db_findnode(db, name, 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) {
+ INSIST(!dns_rdataset_isassociated(&rdataset));
+ return (ISC_R_SUCCESS);
+ }
+ if (result != ISC_R_SUCCESS) {
+ INSIST(!dns_rdataset_isassociated(&rdataset));
+ 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);
+ result = dns_rdata_tostruct(&rdata, &rrsig, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ if (type != dns_rdatatype_dnskey) {
+ bool warn = false, deleted = false;
+ if (delsig_ok(&rrsig, keys, nkeys, &warn)) {
+ result = update_one_rr(db, ver, zonediff->diff,
+ DNS_DIFFOP_DELRESIGN, name,
+ rdataset.ttl, &rdata);
+ if (result != ISC_R_SUCCESS)
+ break;
+ deleted = true;
+ }
+ if (warn) {
+ /*
+ * At this point, we've got an RRSIG,
+ * which is signed by an inactive key.
+ * An administrator needs to provide a new
+ * key/alg, but until that time, we want to
+ * keep the old RRSIG. Marking the key as
+ * offline will prevent us spinning waiting
+ * for the private part.
+ */
+ if (incremental && !deleted) {
+ result = offline(db, ver, zonediff,
+ name, rdataset.ttl,
+ &rdata);
+ if (result != ISC_R_SUCCESS)
+ break;
+ }
+
+ /*
+ * Log the key id and algorithm of
+ * the inactive key with no replacement
+ */
+ if (zone->log_key_expired_timer <= now) {
+ char origin[DNS_NAME_FORMATSIZE];
+ char algbuf[DNS_NAME_FORMATSIZE];
+ dns_name_format(&zone->origin, origin,
+ sizeof(origin));
+ dns_secalg_format(rrsig.algorithm,
+ algbuf,
+ sizeof(algbuf));
+ dns_zone_log(zone, ISC_LOG_WARNING,
+ "Key %s/%s/%d "
+ "missing or inactive "
+ "and has no replacement: "
+ "retaining signatures.",
+ origin, algbuf,
+ rrsig.keyid);
+ zone->log_key_expired_timer = now +
+ 3600;
+ }
+ }
+ continue;
+ }
+
+ /*
+ * RRSIG(DNSKEY) requires special processing.
+ */
+ found = false;
+ for (i = 0; i < nkeys; i++) {
+ if (rrsig.algorithm == dst_key_alg(keys[i]) &&
+ rrsig.keyid == dst_key_id(keys[i])) {
+ found = true;
+ /*
+ * Mark offline RRSIG(DNSKEY).
+ * We want the earliest offline expire time
+ * iff there is a new offline signature.
+ */
+ if (!dst_key_inactive(keys[i]) &&
+ !dst_key_isprivate(keys[i]))
+ {
+ int64_t timeexpire =
+ dns_time64_from32(rrsig.timeexpire);
+ if (timewarn != 0 &&
+ timewarn > timeexpire)
+ timewarn = timeexpire;
+ if (rdata.flags & DNS_RDATA_OFFLINE) {
+ if (timemaybe == 0 ||
+ timemaybe > timeexpire)
+ timemaybe = timeexpire;
+ break;
+ }
+ if (timewarn == 0)
+ timewarn = timemaybe;
+ if (timewarn == 0 ||
+ timewarn > timeexpire)
+ timewarn = timeexpire;
+ result = offline(db, ver, zonediff,
+ name, rdataset.ttl,
+ &rdata);
+ break;
+ }
+ result = update_one_rr(db, ver, zonediff->diff,
+ DNS_DIFFOP_DELRESIGN,
+ name, rdataset.ttl,
+ &rdata);
+ break;
+ }
+ }
+
+ /*
+ * If there is not a matching DNSKEY then
+ * delete the RRSIG.
+ */
+ if (!found)
+ result = update_one_rr(db, ver, zonediff->diff,
+ DNS_DIFFOP_DELRESIGN, name,
+ rdataset.ttl, &rdata);
+ if (result != ISC_R_SUCCESS)
+ break;
+ }
+
+ dns_rdataset_disassociate(&rdataset);
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ if (timewarn > 0) {
+#if defined(STDTIME_ON_32BITS)
+ isc_stdtime_t stdwarn = (isc_stdtime_t)timewarn;
+ if (timewarn == stdwarn)
+#endif
+ set_key_expiry_warning(zone, (isc_stdtime_t)timewarn,
+ now);
+#if defined(STDTIME_ON_32BITS)
+ else
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "key expiry warning time out of range");
+#endif
+ }
+ 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, bool check_ksk,
+ bool keyset_kskonly)
+{
+ 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, j;
+
+ dns_rdataset_init(&rdataset);
+ isc_buffer_init(&buffer, data, sizeof(data));
+
+ if (type == dns_rdatatype_nsec3)
+ result = dns_db_findnsec3node(db, name, false, &node);
+ else
+ result = dns_db_findnode(db, name, 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) {
+ INSIST(!dns_rdataset_isassociated(&rdataset));
+ return (ISC_R_SUCCESS);
+ }
+ if (result != ISC_R_SUCCESS) {
+ INSIST(!dns_rdataset_isassociated(&rdataset));
+ goto failure;
+ }
+
+ for (i = 0; i < nkeys; i++) {
+ bool both = false;
+
+ if (!dst_key_isprivate(keys[i]))
+ continue;
+ if (dst_key_inactive(keys[i])) /* Should be redundant. */
+ continue;
+
+ if (check_ksk && !REVOKE(keys[i])) {
+ bool have_ksk, have_nonksk;
+ if (KSK(keys[i])) {
+ have_ksk = true;
+ have_nonksk = false;
+ } else {
+ have_ksk = false;
+ have_nonksk = true;
+ }
+ for (j = 0; j < nkeys; j++) {
+ if (j == i || ALG(keys[i]) != ALG(keys[j]))
+ continue;
+ if (!dst_key_isprivate(keys[j]))
+ continue;
+ if (dst_key_inactive(keys[j])) /* SBR */
+ continue;
+ if (REVOKE(keys[j]))
+ continue;
+ if (KSK(keys[j]))
+ have_ksk = true;
+ else
+ have_nonksk = true;
+ both = have_ksk && have_nonksk;
+ if (both)
+ break;
+ }
+ }
+ if (both) {
+ if (type == dns_rdatatype_dnskey) {
+ if (!KSK(keys[i]) && keyset_kskonly)
+ continue;
+ } else if (KSK(keys[i]))
+ continue;
+ } else if (REVOKE(keys[i]) && type != dns_rdatatype_dnskey)
+ continue;
+
+ /* Calculate the signature, creating a RRSIG RDATA. */
+ isc_buffer_clear(&buffer);
+ 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);
+ isc_buffer_init(&buffer, data, sizeof(data));
+ }
+
+ 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 *me = "zone_resigninc";
+ dns_db_t *db = NULL;
+ dns_dbversion_t *version = NULL;
+ dns_diff_t _sig_diff;
+ dns__zonediff_t zonediff;
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+ dns_rdataset_t rdataset;
+ dns_rdatatype_t covers;
+ dst_key_t *zone_keys[DNS_MAXZONEKEYS];
+ bool check_ksk, keyset_kskonly = false;
+ isc_result_t result;
+ isc_stdtime_t now, inception, soaexpire, expire, stop;
+ uint32_t jitter, sigvalidityinterval;
+ unsigned int i;
+ unsigned int nkeys = 0;
+ unsigned int resign;
+
+ ENTER;
+
+ dns_rdataset_init(&rdataset);
+ dns_diff_init(zone->mctx, &_sig_diff);
+ zonediff_init(&zonediff, &_sig_diff);
+
+ /*
+ * Zone is frozen or automatic resigning is disabled.
+ * Pause for 5 minutes.
+ */
+ if (zone->update_disabled ||
+ DNS_ZONEKEY_OPTION(zone, DNS_ZONEKEY_NORESIGN))
+ {
+ 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",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ isc_stdtime_get(&now);
+
+ result = dns__zone_findkeys(zone, db, version, now, zone->mctx,
+ DNS_MAXZONEKEYS, zone_keys, &nkeys);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_resigninc:dns__zone_findkeys -> %s",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ sigvalidityinterval = zone->sigvalidityinterval;
+ inception = now - 3600; /* Allow for clock skew. */
+ soaexpire = now + sigvalidityinterval;
+ /*
+ * 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.
+ */
+ if (sigvalidityinterval >= 3600U) {
+ isc_random_get(&jitter);
+ if (sigvalidityinterval > 7200U) {
+ jitter %= 3600;
+ } else {
+ jitter %= 1200;
+ }
+ expire = soaexpire - jitter - 1;
+ } else {
+ expire = soaexpire - 1;
+ }
+ stop = now + 5;
+
+ check_ksk = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_UPDATECHECKKSK);
+ keyset_kskonly = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_DNSKEYKSKONLY);
+
+ name = dns_fixedname_initname(&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",
+ dns_result_totext(result));
+ }
+
+ i = 0;
+ while (result == ISC_R_SUCCESS) {
+ resign = rdataset.resign - zone->sigresigninginterval;
+ covers = rdataset.covers;
+ dns_rdataset_disassociate(&rdataset);
+
+ /*
+ * 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)
+ break;
+
+ result = del_sigs(zone, db, version, name, covers, &zonediff,
+ zone_keys, nkeys, now, true);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_resigninc:del_sigs -> %s",
+ dns_result_totext(result));
+ break;
+ }
+
+ result = add_sigs(db, version, name, covers, zonediff.diff,
+ zone_keys, nkeys, zone->mctx, inception,
+ expire, check_ksk, keyset_kskonly);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_resigninc:add_sigs -> %s",
+ dns_result_totext(result));
+ break;
+ }
+ result = dns_db_getsigningtime(db, &rdataset, name);
+ 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",
+ 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,
+ &zonediff, zone_keys, nkeys, now, true);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_resigninc:del_sigs -> %s",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ /*
+ * Did we change anything in the zone?
+ */
+ if (ISC_LIST_EMPTY(zonediff.diff->tuples)) {
+ /*
+ * Commit the changes if any key has been marked as offline.
+ */
+ if (zonediff.offline)
+ dns_db_closeversion(db, &version, true);
+ goto failure;
+ }
+
+ /* Increment SOA serial if we have made changes */
+ result = update_soa_serial(db, version, zonediff.diff, zone->mctx,
+ zone->updatemethod);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_resigninc:update_soa_serial -> %s",
+ 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,
+ zonediff.diff, zone_keys, nkeys, zone->mctx,
+ inception, soaexpire, check_ksk, keyset_kskonly);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_resigninc:add_sigs -> %s",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ /* Write changes to journal file. */
+ CHECK(zone_journal(zone, zonediff.diff, NULL, "zone_resigninc"));
+
+ /* Everything has succeeded. Commit the changes. */
+ dns_db_closeversion(db, &version, 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(db, &version, 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);
+ }
+
+ INSIST(version == NULL);
+}
+
+static isc_result_t
+next_active(dns_db_t *db, dns_dbversion_t *version, dns_name_t *oldname,
+ dns_name_t *newname, bool 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 bool
+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) {
+ INSIST(!dns_rdataset_isassociated(&rdataset));
+ return (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 (true);
+ }
+ dns_rdata_reset(&rdata);
+ }
+ dns_rdataset_disassociate(&rdataset);
+ return (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, bool 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];
+
+ next = dns_fixedname_initname(&fixed);
+
+ CHECK(next_active(db, version, name, next, bottom));
+ CHECK(dns_nsec_buildrdata(db, version, node, next, nsecbuffer,
+ &rdata));
+ CHECK(update_one_rr(db, version, diff, DNS_DIFFOP_ADD, name, ttl,
+ &rdata));
+ failure:
+ return (result);
+}
+
+static isc_result_t
+check_if_bottom_of_zone(dns_db_t *db, dns_dbnode_t *node,
+ dns_dbversion_t *version, bool *is_bottom_of_zone)
+{
+ isc_result_t result;
+ dns_rdatasetiter_t *iterator = NULL;
+ dns_rdataset_t rdataset;
+ bool seen_soa = false, seen_ns = false, seen_dname = false;
+
+ REQUIRE(is_bottom_of_zone != NULL);
+
+ 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);
+ switch (rdataset.type) {
+ case dns_rdatatype_soa:
+ seen_soa = true;
+ break;
+ case dns_rdatatype_ns:
+ seen_ns = true;
+ break;
+ case dns_rdatatype_dname:
+ seen_dname = true;
+ break;
+ }
+ dns_rdataset_disassociate(&rdataset);
+ }
+ if (result != ISC_R_NOMORE) {
+ goto failure;
+ }
+ if ((seen_ns && !seen_soa) || seen_dname) {
+ *is_bottom_of_zone = true;
+ }
+ result = ISC_R_SUCCESS;
+
+ failure:
+ dns_rdatasetiter_destroy(&iterator);
+
+ 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, bool build_nsec3,
+ bool build_nsec, dst_key_t *key,
+ isc_stdtime_t inception, isc_stdtime_t expire,
+ unsigned int minimum, bool is_ksk,
+ bool keyset_kskonly, bool is_bottom_of_zone,
+ dns_diff_t *diff, 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];
+ bool seen_soa, seen_ns, seen_rr, seen_dname, seen_nsec,
+ seen_nsec3, seen_ds;
+
+ 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 = 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 = true;
+ else if (rdataset.type == dns_rdatatype_ns)
+ seen_ns = true;
+ else if (rdataset.type == dns_rdatatype_ds)
+ seen_ds = true;
+ else if (rdataset.type == dns_rdatatype_dname)
+ seen_dname = true;
+ else if (rdataset.type == dns_rdatatype_nsec)
+ seen_nsec = true;
+ else if (rdataset.type == dns_rdatatype_nsec3)
+ seen_nsec3 = true;
+ if (rdataset.type != dns_rdatatype_rrsig)
+ seen_rr = true;
+ dns_rdataset_disassociate(&rdataset);
+ }
+ if (result != ISC_R_NOMORE)
+ goto failure;
+ /*
+ * Going from insecure to NSEC3.
+ * Don't generate NSEC3 records for NSEC3 records.
+ */
+ if (build_nsec3 && !seen_nsec3 && seen_rr) {
+ bool 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 a NSEC record except at the origin.
+ */
+ if (!dns_name_equal(name, dns_db_origin(db))) {
+ CHECK(add_nsec(db, version, name, node, minimum,
+ is_bottom_of_zone, 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 (rdataset.type == dns_rdatatype_dnskey) {
+ if (!is_ksk && keyset_kskonly)
+ goto next_rdataset;
+ } else if (is_ksk) {
+ /*
+ * CDS and CDNSKEY are signed with KSK (RFC 7344, 4.1).
+ */
+ if (rdataset.type != dns_rdatatype_cds &&
+ rdataset.type != dns_rdatatype_cdnskey)
+ goto next_rdataset;
+ }
+ if (seen_ns && !seen_soa &&
+ rdataset.type != dns_rdatatype_ds &&
+ rdataset.type != dns_rdatatype_nsec)
+ goto next_rdataset;
+ if (signed_with_key(db, node, version, rdataset.type, key))
+ goto next_rdataset;
+ /* Calculate the signature, creating a RRSIG RDATA. */
+ isc_buffer_clear(&buffer);
+ 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;
+ failure:
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ if (iterator != NULL)
+ dns_rdatasetiter_destroy(&iterator);
+ return (result);
+}
+
+/*
+ * If 'update_only' is set then don't create a NSEC RRset if it doesn't exist.
+ */
+static isc_result_t
+updatesecure(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name,
+ dns_ttl_t minimum, bool update_only, dns_diff_t *diff)
+{
+ isc_result_t result;
+ dns_rdataset_t rdataset;
+ dns_dbnode_t *node = NULL;
+
+ CHECK(dns_db_getoriginnode(db, &node));
+ if (update_only) {
+ dns_rdataset_init(&rdataset);
+ result = dns_db_findrdataset(db, node, version,
+ dns_rdatatype_nsec,
+ dns_rdatatype_none,
+ 0, &rdataset, NULL);
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ if (result == ISC_R_NOTFOUND)
+ goto success;
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+ }
+ CHECK(delete_nsec(db, version, node, name, diff));
+ CHECK(add_nsec(db, version, name, node, minimum, false, diff));
+ success:
+ result = ISC_R_SUCCESS;
+ failure:
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ return (result);
+}
+
+static isc_result_t
+updatesignwithkey(dns_zone_t *zone, dns_signing_t *signing,
+ dns_dbversion_t *version, bool build_nsec3,
+ dns_ttl_t minimum, 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];
+ bool seen_done = false;
+ bool have_rr = 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,
+ zone->privatetype, dns_rdatatype_none,
+ 0, &rdataset, NULL);
+ if (result == ISC_R_NOTFOUND) {
+ INSIST(!dns_rdataset_isassociated(&rdataset));
+ result = ISC_R_SUCCESS;
+ goto failure;
+ }
+ if (result != ISC_R_SUCCESS) {
+ INSIST(!dns_rdataset_isassociated(&rdataset));
+ goto failure;
+ }
+ for (result = dns_rdataset_first(&rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rdataset)) {
+ dns_rdataset_current(&rdataset, &rdata);
+ /*
+ * If we don't match the algorithm or keyid skip the record.
+ */
+ if (rdata.length != 5 ||
+ rdata.data[0] != signing->algorithm ||
+ rdata.data[1] != ((signing->keyid >> 8) & 0xff) ||
+ rdata.data[2] != (signing->keyid & 0xff)) {
+ have_rr = true;
+ dns_rdata_reset(&rdata);
+ continue;
+ }
+ /*
+ * We have a match. If we were signing (!signing->deleteit)
+ * and we already have a record indicating that we have
+ * finished signing (rdata.data[4] != 0) then keep it.
+ * Otherwise it needs to be deleted as we have removed all
+ * the signatures (signing->deleteit), so any record indicating
+ * completion is now out of date, or we have finished signing
+ * with the new record so we no longer need to remember that
+ * we need to sign the zone with the matching key across a
+ * nameserver re-start.
+ */
+ if (!signing->deleteit && rdata.data[4] != 0) {
+ seen_done = true;
+ have_rr = true;
+ } else
+ CHECK(update_one_rr(signing->db, version, diff,
+ DNS_DIFFOP_DEL, &zone->origin,
+ rdataset.ttl, &rdata));
+ dns_rdata_reset(&rdata);
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ if (!signing->deleteit && !seen_done) {
+ /*
+ * If we were signing then we need to indicate that we have
+ * finished signing the zone with this key. If it is already
+ * there we don't need to add it a second time.
+ */
+ 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 = zone->privatetype;
+ rdata.rdclass = dns_db_class(signing->db);
+ CHECK(update_one_rr(signing->db, version, diff, DNS_DIFFOP_ADD,
+ &zone->origin, rdataset.ttl, &rdata));
+ } else if (!have_rr) {
+ dns_name_t *origin = dns_db_origin(signing->db);
+ /*
+ * Rebuild the NSEC/NSEC3 record for the origin as we no
+ * longer have any private records.
+ */
+ if (build_nsec3)
+ CHECK(dns_nsec3_addnsec3s(signing->db, version, origin,
+ minimum, false, diff));
+ CHECK(updatesecure(signing->db, version, origin, minimum,
+ true, diff));
+ }
+
+ failure:
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ if (node != NULL)
+ dns_db_detachnode(signing->db, &node);
+ return (result);
+}
+
+/*
+ * Called from zone_nsec3chain() in order to update zone records indicating
+ * processing status of given NSEC3 chain:
+ *
+ * - If the supplied dns_nsec3chain_t structure has been fully processed
+ * (which is indicated by "active" being set to false):
+ *
+ * - remove all NSEC3PARAM records matching the relevant NSEC3 chain,
+ *
+ * - remove all private-type records containing NSEC3PARAM RDATA matching
+ * the relevant NSEC3 chain.
+ *
+ * - If the supplied dns_nsec3chain_t structure has not been fully processed
+ * (which is indicated by "active" being set to true), only remove the
+ * NSEC3PARAM record which matches the relevant NSEC3 chain and has the
+ * "flags" field set to 0.
+ *
+ * - If given NSEC3 chain is being added, add an NSEC3PARAM record contained
+ * in the relevant private-type record, but with the "flags" field set to
+ * 0, indicating that this NSEC3 chain is now complete for this zone.
+ *
+ * Note that this function is called at different processing stages for NSEC3
+ * chain additions vs. removals and needs to handle all cases properly.
+ */
+static isc_result_t
+fixup_nsec3param(dns_db_t *db, dns_dbversion_t *ver, dns_nsec3chain_t *chain,
+ bool active, dns_rdatatype_t privatetype,
+ 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;
+ bool nseconly = false, nsec3ok = false;
+
+ 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 try_private;
+ 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;
+
+ dns_rdataset_disassociate(&rdataset);
+
+ try_private:
+
+ if (active)
+ goto add;
+
+ result = dns_nsec_nseconly(db, ver, &nseconly);
+ nsec3ok = (result == ISC_R_SUCCESS && !nseconly);
+
+ /*
+ * Delete all private records which match that in nsec3chain.
+ */
+ result = dns_db_findrdataset(db, node, ver, privatetype,
+ 0, 0, &rdataset, NULL);
+ if (result == ISC_R_NOTFOUND)
+ goto add;
+ 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 private = DNS_RDATA_INIT;
+ unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE];
+
+ dns_rdataset_current(&rdataset, &private);
+ if (!dns_nsec3param_fromprivate(&private, &rdata,
+ buf, sizeof(buf)))
+ continue;
+ CHECK(dns_rdata_tostruct(&rdata, &nsec3param, NULL));
+
+ if ((!nsec3ok &&
+ (nsec3param.flags & DNS_NSEC3FLAG_INITIAL) != 0) ||
+ nsec3param.hash != chain->nsec3param.hash ||
+ 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, &private));
+ 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,
+ bool *answer)
+{
+ 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 = 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_SUCCESS) {
+ dns_rdataset_disassociate(&rdataset);
+ dns_db_detachnode(db, &node);
+ return (result);
+ }
+ if (result != ISC_R_NOTFOUND) {
+ dns_db_detachnode(db, &node);
+ return (result);
+ }
+
+ result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3param,
+ 0, 0, &rdataset, NULL);
+ if (result == ISC_R_NOTFOUND) {
+ *answer = 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 = true;
+ result = ISC_R_SUCCESS;
+ }
+
+ failure:
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ dns_db_detachnode(db, &node);
+ return (result);
+}
+
+/*%
+ * Add/remove DNSSEC signatures for the list of "raw" zone changes supplied in
+ * 'diff'. Gradually remove tuples from 'diff' and append them to 'zonediff'
+ * along with tuples representing relevant signature changes.
+ */
+isc_result_t
+dns__zone_updatesigs(dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *version,
+ dst_key_t *zone_keys[], unsigned int nkeys,
+ dns_zone_t *zone, isc_stdtime_t inception,
+ isc_stdtime_t expire, isc_stdtime_t now,
+ bool check_ksk, bool keyset_kskonly,
+ dns__zonediff_t *zonediff)
+{
+ dns_difftuple_t *tuple;
+ isc_result_t result;
+
+ for (tuple = ISC_LIST_HEAD(diff->tuples);
+ tuple != NULL;
+ tuple = ISC_LIST_HEAD(diff->tuples)) {
+ result = del_sigs(zone, db, version, &tuple->name,
+ tuple->rdata.type, zonediff,
+ zone_keys, nkeys, now, false);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "dns__zone_updatesigs:del_sigs -> %s",
+ dns_result_totext(result));
+ return (result);
+ }
+ result = add_sigs(db, version, &tuple->name,
+ tuple->rdata.type, zonediff->diff,
+ zone_keys, nkeys, zone->mctx, inception,
+ expire, check_ksk, keyset_kskonly);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "dns__zone_updatesigs:add_sigs -> %s",
+ dns_result_totext(result));
+ return (result);
+ }
+
+ do {
+ dns_difftuple_t *next = ISC_LIST_NEXT(tuple, link);
+ while (next != NULL &&
+ (tuple->rdata.type != next->rdata.type ||
+ !dns_name_equal(&tuple->name, &next->name)))
+ next = ISC_LIST_NEXT(next, link);
+ ISC_LIST_UNLINK(diff->tuples, tuple, link);
+ dns_diff_appendminimal(zonediff->diff, &tuple);
+ INSIST(tuple == NULL);
+ tuple = next;
+ } while (tuple != NULL);
+ }
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Incrementally build and sign a new NSEC3 chain using the parameters
+ * requested.
+ */
+static void
+zone_nsec3chain(dns_zone_t *zone) {
+ const char *me = "zone_nsec3chain";
+ 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__zonediff_t zonediff;
+ 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[DNS_MAXZONEKEYS];
+ int32_t signatures;
+ bool check_ksk, keyset_kskonly;
+ bool delegation;
+ bool first;
+ isc_result_t result;
+ isc_stdtime_t now, inception, soaexpire, expire;
+ uint32_t jitter, sigvalidityinterval;
+ unsigned int i;
+ unsigned int nkeys = 0;
+ uint32_t nodes;
+ bool unsecure = false;
+ bool seen_soa, seen_ns, seen_dname, seen_ds;
+ bool seen_nsec, seen_nsec3, seen_rr;
+ dns_rdatasetiter_t *iterator = NULL;
+ bool buildnsecchain;
+ bool updatensec = false;
+ dns_rdatatype_t privatetype = zone->privatetype;
+
+ ENTER;
+
+ dns_rdataset_init(&rdataset);
+ name = dns_fixedname_initname(&fixed);
+ nextname = dns_fixedname_initname(&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);
+ zonediff_init(&zonediff, &_sig_diff);
+ 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);
+ /*
+ * This function is called when zone timer fires, after the latter gets
+ * set by zone_addnsec3chain(). If the action triggering the call to
+ * zone_addnsec3chain() is closely followed by a zone deletion request,
+ * it might turn out that the timer thread will not be woken up until
+ * after the zone is deleted by rmzone(), which calls dns_db_detach()
+ * for zone->db, causing the latter to become NULL. Return immediately
+ * if that happens.
+ */
+ if (zone->db != NULL) {
+ dns_db_attach(zone->db, &db);
+ }
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
+ if (db == NULL) {
+ return;
+ }
+
+ result = dns_db_newversion(db, &version);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_nsec3chain:dns_db_newversion -> %s",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ isc_stdtime_get(&now);
+
+ result = dns__zone_findkeys(zone, db, version, now, zone->mctx,
+ DNS_MAXZONEKEYS, zone_keys, &nkeys);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_nsec3chain:dns__zone_findkeys -> %s",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ sigvalidityinterval = dns_zone_getsigvalidityinterval(zone);
+ inception = now - 3600; /* Allow for clock skew. */
+ soaexpire = now + sigvalidityinterval;
+
+ /*
+ * 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.
+ */
+ if (sigvalidityinterval >= 3600U) {
+ isc_random_get(&jitter);
+ if (sigvalidityinterval > 7200U) {
+ jitter %= 3600;
+ } else {
+ jitter %= 1200;
+ }
+ expire = soaexpire - jitter - 1;
+ } else {
+ expire = soaexpire - 1;
+ }
+
+ check_ksk = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_UPDATECHECKKSK);
+ keyset_kskonly = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_DNSKEYKSKONLY);
+
+ /*
+ * We keep pulling nodes off each iterator 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 = true;
+
+ if (nsec3chain != NULL)
+ nsec3chain->save_delete_nsec = nsec3chain->delete_nsec;
+ /*
+ * Generate new NSEC3 chains first.
+ *
+ * The following while loop iterates over nodes in the zone database,
+ * updating the NSEC3 chain by calling dns_nsec3_addnsec3() for each of
+ * them. Once all nodes are processed, the "delete_nsec" field is
+ * consulted to check whether we are supposed to remove NSEC records
+ * from the zone database; if so, the database iterator is reset to
+ * point to the first node and the loop traverses all of them again,
+ * this time removing NSEC records. If we hit a node which is obscured
+ * by a delegation or a DNAME, nodes are skipped over until we find one
+ * that is not obscured by the same obscuring name and then normal
+ * processing is resumed.
+ *
+ * The above is repeated until all requested NSEC3 chain changes are
+ * applied or when we reach the limits for this quantum, whichever
+ * happens first.
+ *
+ * Note that the "signatures" variable is only used here to limit the
+ * amount of work performed. Actual DNSSEC signatures are only
+ * generated by dns__zone_updatesigs() calls later in this function.
+ */
+ 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;
+
+ dns_dbiterator_current(nsec3chain->dbiterator, &node, name);
+
+ if (nsec3chain->delete_nsec) {
+ delegation = 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 = false;
+ unsecure = false;
+ if (first) {
+ dns_fixedname_t ffound;
+ dns_name_t *found;
+ found = dns_fixedname_initname(&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 = 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 =
+ 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 = true;
+ else if (rdataset.type == dns_rdatatype_ns)
+ seen_ns = true;
+ else if (rdataset.type == dns_rdatatype_dname)
+ seen_dname = true;
+ else if (rdataset.type == dns_rdatatype_ds)
+ seen_ds = true;
+ else if (rdataset.type == dns_rdatatype_nsec)
+ seen_nsec = 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 = true;
+ if (seen_ns && !seen_soa && !seen_ds)
+ unsecure = true;
+ if ((seen_ns && !seen_soa) || seen_dname)
+ delegation = true;
+
+ /*
+ * Process one node.
+ */
+ dns_dbiterator_pause(nsec3chain->dbiterator);
+ result = dns_nsec3_addnsec3(db, version, name,
+ &nsec3chain->nsec3param,
+ zone->minimum, unsecure,
+ &nsec3_diff);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:"
+ "dns_nsec3_addnsec3 -> %s",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ /*
+ * 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 = false;
+ dns_db_detachnode(db, &node);
+ do {
+ result = dns_dbiterator_next(nsec3chain->dbiterator);
+
+ if (result == ISC_R_NOMORE && nsec3chain->delete_nsec) {
+ dns_dbiterator_pause(nsec3chain->dbiterator);
+ CHECK(fixup_nsec3param(db, version, nsec3chain,
+ false, privatetype,
+ &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,
+ true,
+ privatetype,
+ &param_diff));
+ nsec3chain->delete_nsec = true;
+ goto same_addchain;
+ }
+ CHECK(fixup_nsec3param(db, version, nsec3chain,
+ false, privatetype,
+ &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",
+ 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 = true;
+ continue;
+
+ next_addchain:
+ dns_dbiterator_pause(nsec3chain->dbiterator);
+ nsec3chain = nextnsec3chain;
+ first = true;
+ if (nsec3chain != NULL)
+ nsec3chain->save_delete_nsec = nsec3chain->delete_nsec;
+ }
+
+ if (nsec3chain != NULL)
+ goto skip_removals;
+
+ /*
+ * Process removals.
+ *
+ * This is a counterpart of the above while loop which takes care of
+ * removing an NSEC3 chain. It starts with determining whether the
+ * zone needs to switch from NSEC3 to NSEC; if so, it first builds an
+ * NSEC chain by iterating over all nodes in the zone database and only
+ * then goes on to remove NSEC3 records be iterating over all nodes
+ * again and calling deletematchingnsec3() for each of them; otherwise,
+ * it starts removing NSEC3 records immediately. Rules for processing
+ * obscured nodes and interrupting work are the same as for the while
+ * loop above.
+ */
+ LOCK_ZONE(zone);
+ nsec3chain = ISC_LIST_HEAD(zone->nsec3chain);
+ UNLOCK_ZONE(zone);
+ first = true;
+ buildnsecchain = 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)
+ {
+ result = need_nsec_chain(db, version,
+ &nsec3chain->nsec3param,
+ &buildnsecchain);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_nsec3chain:"
+ "need_nsec_chain -> %s",
+ dns_result_totext(result));
+ goto failure;
+ }
+ }
+
+ if (first)
+ dns_zone_log(zone, ISC_LOG_DEBUG(3), "zone_nsec3chain:"
+ "buildnsecchain = %u\n", buildnsecchain);
+
+ dns_dbiterator_current(nsec3chain->dbiterator, &node, name);
+ delegation = false;
+
+ if (!buildnsecchain) {
+ /*
+ * Delete the NSEC3PARAM record matching this chain.
+ */
+ if (first) {
+ result = fixup_nsec3param(db, version,
+ nsec3chain,
+ true, privatetype,
+ &param_diff);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_nsec3chain:"
+ "fixup_nsec3param -> %s",
+ dns_result_totext(result));
+ goto failure;
+ }
+ }
+
+ /*
+ * Delete the NSEC3 records.
+ */
+ result = deletematchingnsec3(db, version, node, name,
+ &nsec3chain->nsec3param,
+ &nsec3_diff);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_nsec3chain:"
+ "deletematchingnsec3 -> %s",
+ dns_result_totext(result));
+ goto failure;
+ }
+ goto next_removenode;
+ }
+
+ if (first) {
+ dns_fixedname_t ffound;
+ dns_name_t *found;
+ found = dns_fixedname_initname(&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 = 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 = 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 = true;
+ else if (rdataset.type == dns_rdatatype_ns)
+ seen_ns = true;
+ else if (rdataset.type == dns_rdatatype_dname)
+ seen_dname = true;
+ else if (rdataset.type == dns_rdatatype_nsec)
+ seen_nsec = true;
+ else if (rdataset.type == dns_rdatatype_nsec3)
+ seen_nsec3 = true;
+ if (rdataset.type != dns_rdatatype_rrsig)
+ seen_rr = 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 = true;
+
+ /*
+ * Add a NSEC record except at the origin.
+ */
+ if (!dns_name_equal(name, dns_db_origin(db))) {
+ dns_dbiterator_pause(nsec3chain->dbiterator);
+ CHECK(add_nsec(db, version, name, node, zone->minimum,
+ delegation, &nsec_diff));
+ signatures--;
+ }
+
+ next_removenode:
+ first = 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 = 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);
+ result = fixup_nsec3param(db, version,
+ nsec3chain, false,
+ privatetype,
+ &param_diff);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_nsec3chain:"
+ "fixup_nsec3param -> %s",
+ dns_result_totext(result));
+ goto failure;
+ }
+ goto next_removechain;
+ } else if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_nsec3chain:"
+ "dns_dbiterator_next -> %s",
+ 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 = false;
+ first = true;
+ continue;
+
+ next_removechain:
+ dns_dbiterator_pause(nsec3chain->dbiterator);
+ nsec3chain = nextnsec3chain;
+ first = true;
+ }
+
+ skip_removals:
+ /*
+ * We may need to update the NSEC/NSEC3 records for the zone apex.
+ */
+ if (!ISC_LIST_EMPTY(param_diff.tuples)) {
+ bool rebuild_nsec = false,
+ rebuild_nsec3 = false;
+ result = dns_db_getoriginnode(db, &node);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ result = dns_db_allrdatasets(db, node, version, 0, &iterator);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:"
+ "dns_db_allrdatasets -> %s",
+ dns_result_totext(result));
+ goto failure;
+ }
+ 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_nsec)
+ rebuild_nsec = true;
+ if (rdataset.type == dns_rdatatype_nsec3param)
+ rebuild_nsec3 = true;
+ dns_rdataset_disassociate(&rdataset);
+ }
+ dns_rdatasetiter_destroy(&iterator);
+ dns_db_detachnode(db, &node);
+
+ if (rebuild_nsec) {
+ if (nsec3chain != NULL)
+ dns_dbiterator_pause(nsec3chain->dbiterator);
+
+ result = updatesecure(db, version, &zone->origin,
+ zone->minimum, true,
+ &nsec_diff);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_nsec3chain:"
+ "updatesecure -> %s",
+ dns_result_totext(result));
+ goto failure;
+ }
+ }
+
+ if (rebuild_nsec3) {
+ if (nsec3chain != NULL)
+ dns_dbiterator_pause(nsec3chain->dbiterator);
+
+ result = dns_nsec3_addnsec3s(db, version,
+ dns_db_origin(db),
+ zone->minimum, false,
+ &nsec3_diff);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_nsec3chain:"
+ "dns_nsec3_addnsec3s -> %s",
+ dns_result_totext(result));
+ goto failure;
+ }
+ }
+ }
+
+ /*
+ * Add / update signatures for the NSEC3 records.
+ */
+ if (nsec3chain != NULL)
+ dns_dbiterator_pause(nsec3chain->dbiterator);
+ result = dns__zone_updatesigs(&nsec3_diff, db, version, zone_keys,
+ nkeys, zone, inception, expire, now,
+ check_ksk, keyset_kskonly, &zonediff);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:"
+ "dns__zone_updatesigs -> %s",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ /*
+ * We have changed the NSEC3PARAM or private RRsets
+ * above so we need to update the signatures.
+ */
+ result = dns__zone_updatesigs(&param_diff, db, version, zone_keys,
+ nkeys, zone, inception, expire, now,
+ check_ksk, keyset_kskonly, &zonediff);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:"
+ "dns__zone_updatesigs -> %s",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ if (updatensec) {
+ result = updatesecure(db, version, &zone->origin,
+ zone->minimum, false, &nsec_diff);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:"
+ "updatesecure -> %s",
+ dns_result_totext(result));
+ goto failure;
+ }
+ }
+
+ result = dns__zone_updatesigs(&nsec_diff, db, version, zone_keys,
+ nkeys, zone, inception, expire, now,
+ check_ksk, keyset_kskonly, &zonediff);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:"
+ "dns__zone_updatesigs -> %s",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ /*
+ * If we made no effective changes to the zone then we can just
+ * cleanup otherwise we need to increment the serial.
+ */
+ if (ISC_LIST_EMPTY(zonediff.diff->tuples)) {
+ /*
+ * No need to call dns_db_closeversion() here as it is
+ * called with commit = true below.
+ */
+ goto done;
+ }
+
+ result = del_sigs(zone, db, version, &zone->origin, dns_rdatatype_soa,
+ &zonediff, zone_keys, nkeys, now, false);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:"
+ "del_sigs -> %s", dns_result_totext(result));
+ goto failure;
+ }
+
+ result = update_soa_serial(db, version, zonediff.diff, zone->mctx,
+ zone->updatemethod);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:"
+ "update_soa_serial -> %s",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ result = add_sigs(db, version, &zone->origin, dns_rdatatype_soa,
+ zonediff.diff, zone_keys, nkeys, zone->mctx,
+ inception, soaexpire, check_ksk, keyset_kskonly);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:"
+ "add_sigs -> %s", dns_result_totext(result));
+ goto failure;
+ }
+
+ /* Write changes to journal file. */
+ CHECK(zone_journal(zone, zonediff.diff, NULL, "zone_nsec3chain"));
+
+ LOCK_ZONE(zone);
+ zone_needdump(zone, DNS_DUMP_DELAY);
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NEEDNOTIFY);
+ 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.
+ * Unconditionally commit as zonediff.offline not checked above.
+ */
+ dns_db_closeversion(db, &version, 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:
+ if (result != ISC_R_SUCCESS)
+ dns_zone_log(zone, ISC_LOG_ERROR, "zone_nsec3chain: %s",
+ dns_result_totext(result));
+ /*
+ * 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 (node != NULL)
+ dns_db_detachnode(db, &node);
+ if (version != NULL) {
+ dns_db_closeversion(db, &version, 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 interval;
+ if (zone->update_disabled || result != ISC_R_SUCCESS)
+ isc_interval_set(&interval, 60, 0); /* 1 minute */
+ else
+ isc_interval_set(&interval, 0, 10000000); /* 10 ms */
+ isc_time_nowplusinterval(&zone->nsec3chaintime, &interval);
+ } else
+ isc_time_settoepoch(&zone->nsec3chaintime);
+ UNLOCK_ZONE(zone);
+
+ INSIST(version == NULL);
+}
+
+/*%
+ * Delete all RRSIG records with the given algorithm and keyid.
+ * Remove the NSEC record and RRSIGs if nkeys is zero.
+ * If all remaining RRsets are signed with the given algorithm
+ * set *has_algp to true.
+ */
+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,
+ uint16_t keyid, bool *has_algp, dns_diff_t *diff)
+{
+ dns_rdata_rrsig_t rrsig;
+ dns_rdataset_t rdataset;
+ dns_rdatasetiter_t *iterator = NULL;
+ isc_result_t result;
+ bool alg_missed = false;
+ bool alg_found = false;
+
+ char namebuf[DNS_NAME_FORMATSIZE];
+ dns_name_format(name, namebuf, sizeof(namebuf));
+
+ 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)) {
+ bool has_alg = false;
+ 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 (nkeys != 0 &&
+ (rrsig.algorithm != algorithm ||
+ rrsig.keyid != keyid))
+ {
+ if (rrsig.algorithm == algorithm) {
+ has_alg = true;
+ }
+ continue;
+ }
+ CHECK(update_one_rr(db, version, diff,
+ DNS_DIFFOP_DELRESIGN, name,
+ rdataset.ttl, &rdata));
+ }
+ dns_rdataset_disassociate(&rdataset);
+ if (result != ISC_R_NOMORE)
+ break;
+
+ /*
+ * After deleting, if there's still a signature for
+ * 'algorithm', set alg_found; if not, set alg_missed.
+ */
+ if (has_alg) {
+ alg_found = true;
+ } else {
+ alg_missed = true;
+ }
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+
+ /*
+ * Set `has_algp` if the algorithm was found in every RRset:
+ * i.e., found in at least one, and not missing from any.
+ */
+ *has_algp = (alg_found && !alg_missed);
+ 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 *me = "zone_sign";
+ dns_db_t *db = NULL;
+ dns_dbnode_t *node = NULL;
+ dns_dbversion_t *version = NULL;
+ dns_diff_t _sig_diff;
+ dns_diff_t post_diff;
+ dns__zonediff_t zonediff;
+ 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[DNS_MAXZONEKEYS];
+ int32_t signatures;
+ bool check_ksk, keyset_kskonly, is_ksk;
+ bool with_ksk, with_zsk;
+ bool commit = false;
+ bool is_bottom_of_zone;
+ bool build_nsec = false;
+ bool build_nsec3 = false;
+ bool first;
+ isc_result_t result;
+ isc_stdtime_t now, inception, soaexpire, expire;
+ uint32_t jitter, sigvalidityinterval, expiryinterval;
+ unsigned int i, j;
+ unsigned int nkeys = 0;
+ uint32_t nodes;
+
+ ENTER;
+
+ dns_rdataset_init(&rdataset);
+ name = dns_fixedname_initname(&fixed);
+ nextname = dns_fixedname_initname(&nextfixed);
+ dns_diff_init(zone->mctx, &_sig_diff);
+ dns_diff_init(zone->mctx, &post_diff);
+ zonediff_init(&zonediff, &_sig_diff);
+ 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);
+ if (zone->db != NULL)
+ dns_db_attach(zone->db, &db);
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
+ if (db == NULL) {
+ result = ISC_R_FAILURE;
+ goto failure;
+ }
+
+ result = dns_db_newversion(db, &version);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_sign:dns_db_newversion -> %s",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ isc_stdtime_get(&now);
+
+ result = dns__zone_findkeys(zone, db, version, now, zone->mctx,
+ DNS_MAXZONEKEYS, zone_keys, &nkeys);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_sign:dns__zone_findkeys -> %s",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ sigvalidityinterval = dns_zone_getsigvalidityinterval(zone);
+ inception = now - 3600; /* Allow for clock skew. */
+ soaexpire = now + sigvalidityinterval;
+ expiryinterval = dns_zone_getsigresigninginterval(zone);
+ if (expiryinterval > sigvalidityinterval) {
+ expiryinterval = sigvalidityinterval;
+ } else {
+ expiryinterval = sigvalidityinterval - expiryinterval;
+ }
+
+ /*
+ * 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.
+ */
+ if (sigvalidityinterval >= 3600U) {
+ isc_random_get(&jitter);
+ if (sigvalidityinterval > 7200U) {
+ jitter %= expiryinterval;
+ } else {
+ jitter %= 1200;
+ }
+ expire = soaexpire - jitter - 1;
+ } else {
+ expire = soaexpire - 1;
+ }
+
+ /*
+ * We keep pulling nodes off each iterator 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 = true;
+
+ check_ksk = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_UPDATECHECKKSK);
+ keyset_kskonly = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_DNSKEYKSKONLY);
+
+ /* Determine which type of chain to build */
+ CHECK(dns_private_chains(db, version, zone->privatetype,
+ &build_nsec, &build_nsec3));
+
+ /* If neither chain is found, default to NSEC */
+ if (!build_nsec && !build_nsec3)
+ build_nsec = true;
+
+ while (signing != NULL && nodes-- > 0 && signatures > 0) {
+ bool has_alg = false;
+ 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_bottom_of_zone = false;
+
+ if (first && signing->deleteit) {
+ /*
+ * Remove the key we are deleting from consideration.
+ */
+ for (i = 0, j = 0; i < nkeys; i++) {
+ /*
+ * Find the key we want to remove.
+ */
+ if (ALG(zone_keys[i]) == signing->algorithm &&
+ dst_key_id(zone_keys[i]) == signing->keyid)
+ {
+ if (KSK(zone_keys[i]))
+ dst_key_free(&zone_keys[i]);
+ continue;
+ }
+ zone_keys[j] = zone_keys[i];
+ j++;
+ }
+ for (i = j; i < nkeys; i++) {
+ zone_keys[i] = NULL;
+ }
+ nkeys = j;
+ }
+
+ dns_dbiterator_current(signing->dbiterator, &node, name);
+
+ if (signing->deleteit) {
+ dns_dbiterator_pause(signing->dbiterator);
+ CHECK(del_sig(db, version, name, node, nkeys,
+ signing->algorithm, signing->keyid,
+ &has_alg, zonediff.diff));
+ }
+
+ /*
+ * 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;
+ found = dns_fixedname_initname(&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);
+ is_bottom_of_zone = true;
+ goto next_node;
+ }
+ }
+
+ /*
+ * Process one node.
+ */
+ with_ksk = false;
+ with_zsk = false;
+ dns_dbiterator_pause(signing->dbiterator);
+
+ CHECK(check_if_bottom_of_zone(db, node, version,
+ &is_bottom_of_zone));
+
+ for (i = 0; !has_alg && i < nkeys; i++) {
+ bool both = false;
+
+ /*
+ * Find the keys we want to sign with.
+ */
+ if (!dst_key_isprivate(zone_keys[i]))
+ continue;
+ /*
+ * Should be redundant.
+ */
+ if (dst_key_inactive(zone_keys[i]))
+ continue;
+
+ /*
+ * When adding look for the specific key.
+ */
+ if (!signing->deleteit &&
+ (dst_key_alg(zone_keys[i]) != signing->algorithm ||
+ dst_key_id(zone_keys[i]) != signing->keyid))
+ continue;
+
+ /*
+ * When deleting make sure we are properly signed
+ * with the algorithm that was being removed.
+ */
+ if (signing->deleteit &&
+ ALG(zone_keys[i]) != signing->algorithm)
+ continue;
+
+ /*
+ * Do we do KSK processing?
+ */
+ if (check_ksk && !REVOKE(zone_keys[i])) {
+ bool have_ksk, have_nonksk;
+ if (KSK(zone_keys[i])) {
+ have_ksk = true;
+ have_nonksk = false;
+ } else {
+ have_ksk = false;
+ have_nonksk = true;
+ }
+ for (j = 0; j < nkeys; j++) {
+ if (j == i ||
+ ALG(zone_keys[i]) !=
+ ALG(zone_keys[j]))
+ continue;
+ if (!dst_key_isprivate(zone_keys[j]))
+ continue;
+ /*
+ * Should be redundant.
+ */
+ if (dst_key_inactive(zone_keys[j]))
+ continue;
+ if (REVOKE(zone_keys[j]))
+ continue;
+ if (KSK(zone_keys[j]))
+ have_ksk = true;
+ else
+ have_nonksk = true;
+ both = have_ksk && have_nonksk;
+ if (both)
+ break;
+ }
+ }
+ if (both || REVOKE(zone_keys[i]))
+ is_ksk = KSK(zone_keys[i]);
+ else
+ is_ksk = false;
+
+ /*
+ * If deleting signatures, we need to ensure that
+ * the RRset is still signed at least once by a
+ * KSK and a ZSK.
+ */
+ if (signing->deleteit && !is_ksk && with_zsk) {
+ continue;
+ }
+
+ if (signing->deleteit && is_ksk && with_ksk) {
+ continue;
+ }
+
+ CHECK(sign_a_node(db, name, node, version, build_nsec3,
+ build_nsec, zone_keys[i], inception,
+ expire, zone->minimum, is_ksk,
+ (both && keyset_kskonly),
+ is_bottom_of_zone, zonediff.diff,
+ &signatures, zone->mctx));
+ /*
+ * If we are adding we are done. Look for other keys
+ * of the same algorithm if deleting.
+ */
+ if (!signing->deleteit) {
+ break;
+ }
+ if (!is_ksk) {
+ with_zsk = true;
+ }
+ if (KSK(zone_keys[i])) {
+ with_ksk = true;
+ }
+ }
+
+ /*
+ * Go onto next node.
+ */
+ next_node:
+ first = 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);
+ if (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,
+ false,
+ &post_diff);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone,
+ ISC_LOG_ERROR,
+ "updatesecure -> %s",
+ dns_result_totext(result));
+ goto failure;
+ }
+ }
+ result = updatesignwithkey(zone, signing,
+ version,
+ build_nsec3,
+ zone->minimum,
+ &post_diff);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "updatesignwithkey -> %s",
+ dns_result_totext(result));
+ goto failure;
+ }
+ build_nsec = false;
+ goto next_signing;
+ } else if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_sign:dns_dbiterator_next -> %s",
+ dns_result_totext(result));
+ goto failure;
+ } else if (is_bottom_of_zone) {
+ 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 = true;
+ }
+
+ if (ISC_LIST_HEAD(post_diff.tuples) != NULL) {
+ result = dns__zone_updatesigs(&post_diff, db, version,
+ zone_keys, nkeys, zone,
+ inception, expire, now,
+ check_ksk, keyset_kskonly,
+ &zonediff);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR, "zone_sign:"
+ "dns__zone_updatesigs -> %s",
+ dns_result_totext(result));
+ goto failure;
+ }
+ }
+
+ /*
+ * Have we changed anything?
+ */
+ if (ISC_LIST_EMPTY(zonediff.diff->tuples)) {
+ if (zonediff.offline)
+ commit = true;
+ result = ISC_R_SUCCESS;
+ goto pauseall;
+ }
+
+ commit = true;
+
+ result = del_sigs(zone, db, version, &zone->origin, dns_rdatatype_soa,
+ &zonediff, zone_keys, nkeys, now, false);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_sign:del_sigs -> %s",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ result = update_soa_serial(db, version, zonediff.diff, zone->mctx,
+ zone->updatemethod);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_sign:update_soa_serial -> %s",
+ 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,
+ zonediff.diff, zone_keys, nkeys, zone->mctx,
+ inception, soaexpire, check_ksk, keyset_kskonly);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_sign:add_sigs -> %s",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ /*
+ * Write changes to journal file.
+ */
+ CHECK(zone_journal(zone, zonediff.diff, NULL, "zone_sign"));
+
+ pauseall:
+ /*
+ * 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, commit);
+
+ /*
+ * 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);
+
+ if (commit) {
+ LOCK_ZONE(zone);
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NEEDNOTIFY);
+ zone_needdump(zone, DNS_DUMP_DELAY);
+ UNLOCK_ZONE(zone);
+ }
+
+ failure:
+ /*
+ * Pause all dbiterators.
+ */
+ for (signing = ISC_LIST_HEAD(zone->signing);
+ signing != NULL;
+ signing = ISC_LIST_NEXT(signing, link))
+ dns_dbiterator_pause(signing->dbiterator);
+
+ /*
+ * Rollback the cleanup list.
+ */
+ signing = ISC_LIST_HEAD(cleanup);
+ while (signing != NULL) {
+ ISC_LIST_UNLINK(cleanup, signing, link);
+ ISC_LIST_PREPEND(zone->signing, signing, link);
+ dns_dbiterator_first(signing->dbiterator);
+ dns_dbiterator_pause(signing->dbiterator);
+ signing = ISC_LIST_HEAD(cleanup);
+ }
+
+ dns_diff_clear(&_sig_diff);
+
+ for (i = 0; i < nkeys; i++)
+ dst_key_free(&zone_keys[i]);
+
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+
+ if (version != NULL) {
+ dns_db_closeversion(db, &version, false);
+ dns_db_detach(&db);
+ } else if (db != NULL)
+ dns_db_detach(&db);
+
+ if (ISC_LIST_HEAD(zone->signing) != NULL) {
+ isc_interval_t interval;
+ if (zone->update_disabled || result != ISC_R_SUCCESS)
+ isc_interval_set(&interval, 60, 0); /* 1 minute */
+ else
+ isc_interval_set(&interval, 0, 10000000); /* 10 ms */
+ isc_time_nowplusinterval(&zone->signingtime, &interval);
+ } else
+ isc_time_settoepoch(&zone->signingtime);
+
+ INSIST(version == NULL);
+}
+
+static isc_result_t
+normalize_key(dns_rdata_t *rr, dns_rdata_t *target,
+ unsigned char *data, int size)
+{
+ dns_rdata_dnskey_t dnskey;
+ dns_rdata_keydata_t keydata;
+ isc_buffer_t buf;
+ isc_result_t result;
+
+ dns_rdata_reset(target);
+ isc_buffer_init(&buf, data, size);
+
+ switch (rr->type) {
+ case dns_rdatatype_dnskey:
+ result = dns_rdata_tostruct(rr, &dnskey, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ dnskey.flags &= ~DNS_KEYFLAG_REVOKE;
+ dns_rdata_fromstruct(target, rr->rdclass, dns_rdatatype_dnskey,
+ &dnskey, &buf);
+ break;
+ case dns_rdatatype_keydata:
+ result = dns_rdata_tostruct(rr, &keydata, NULL);
+ if (result == ISC_R_UNEXPECTEDEND)
+ return (result);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ dns_keydata_todnskey(&keydata, &dnskey, NULL);
+ dns_rdata_fromstruct(target, rr->rdclass, dns_rdatatype_dnskey,
+ &dnskey, &buf);
+ break;
+ default:
+ INSIST(0);
+ }
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * 'rdset' contains either a DNSKEY rdataset from the zone apex, or
+ * a KEYDATA rdataset from the key zone.
+ *
+ * 'rr' contains either a DNSKEY record, or a KEYDATA record
+ *
+ * After normalizing keys to the same format (DNSKEY, with revoke bit
+ * cleared), return true if a key that matches 'rr' is found in
+ * 'rdset', or false if not.
+ */
+
+static bool
+matchkey(dns_rdataset_t *rdset, dns_rdata_t *rr) {
+ unsigned char data1[4096], data2[4096];
+ dns_rdata_t rdata, rdata1, rdata2;
+ isc_result_t result;
+
+ dns_rdata_init(&rdata);
+ dns_rdata_init(&rdata1);
+ dns_rdata_init(&rdata2);
+
+ result = normalize_key(rr, &rdata1, data1, sizeof(data1));
+ if (result != ISC_R_SUCCESS)
+ return (false);
+
+ for (result = dns_rdataset_first(rdset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(rdset)) {
+ dns_rdata_reset(&rdata);
+ dns_rdataset_current(rdset, &rdata);
+ result = normalize_key(&rdata, &rdata2, data2, sizeof(data2));
+ if (result != ISC_R_SUCCESS)
+ continue;
+ if (dns_rdata_compare(&rdata1, &rdata2) == 0)
+ return (true);
+ }
+
+ return (false);
+}
+
+/*
+ * Calculate the refresh interval for a keydata zone, per
+ * RFC5011: MAX(1 hr,
+ * MIN(15 days,
+ * 1/2 * OrigTTL,
+ * 1/2 * RRSigExpirationInterval))
+ * or for retries: MAX(1 hr,
+ * MIN(1 day,
+ * 1/10 * OrigTTL,
+ * 1/10 * RRSigExpirationInterval))
+ */
+static inline isc_stdtime_t
+refresh_time(dns_keyfetch_t *kfetch, bool retry) {
+ isc_result_t result;
+ uint32_t t;
+ dns_rdataset_t *rdset;
+ dns_rdata_t sigrr = DNS_RDATA_INIT;
+ dns_rdata_sig_t sig;
+ isc_stdtime_t now;
+
+ isc_stdtime_get(&now);
+
+ if (dns_rdataset_isassociated(&kfetch->dnskeysigset))
+ rdset = &kfetch->dnskeysigset;
+ else
+ return (now + dns_zone_mkey_hour);
+
+ result = dns_rdataset_first(rdset);
+ if (result != ISC_R_SUCCESS)
+ return (now + dns_zone_mkey_hour);
+
+ dns_rdataset_current(rdset, &sigrr);
+ result = dns_rdata_tostruct(&sigrr, &sig, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ if (!retry) {
+ t = sig.originalttl / 2;
+
+ if (isc_serial_gt(sig.timeexpire, now)) {
+ uint32_t exp = (sig.timeexpire - now) / 2;
+ if (t > exp)
+ t = exp;
+ }
+
+ if (t > (15 * dns_zone_mkey_day))
+ t = (15 * dns_zone_mkey_day);
+
+ if (t < dns_zone_mkey_hour)
+ t = dns_zone_mkey_hour;
+ } else {
+ t = sig.originalttl / 10;
+
+ if (isc_serial_gt(sig.timeexpire, now)) {
+ uint32_t exp = (sig.timeexpire - now) / 10;
+ if (t > exp)
+ t = exp;
+ }
+
+ if (t > dns_zone_mkey_day)
+ t = dns_zone_mkey_day;
+
+ if (t < dns_zone_mkey_hour)
+ t = dns_zone_mkey_hour;
+ }
+
+ return (now + t);
+}
+
+/*
+ * This routine is called when no changes are needed in a KEYDATA
+ * record except to simply update the refresh timer. Caller should
+ * hold zone lock.
+ */
+static isc_result_t
+minimal_update(dns_keyfetch_t *kfetch, dns_dbversion_t *ver, dns_diff_t *diff)
+{
+ isc_result_t result;
+ isc_buffer_t keyb;
+ unsigned char key_buf[4096];
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_keydata_t keydata;
+ dns_name_t *name;
+ dns_zone_t *zone = kfetch->zone;
+ isc_stdtime_t now;
+
+ name = dns_fixedname_name(&kfetch->name);
+ isc_stdtime_get(&now);
+
+ for (result = dns_rdataset_first(&kfetch->keydataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&kfetch->keydataset)) {
+ dns_rdata_reset(&rdata);
+ dns_rdataset_current(&kfetch->keydataset, &rdata);
+
+ /* Delete old version */
+ CHECK(update_one_rr(kfetch->db, ver, diff, DNS_DIFFOP_DEL,
+ name, 0, &rdata));
+
+ /* Update refresh timer */
+ result = dns_rdata_tostruct(&rdata, &keydata, NULL);
+ if (result == ISC_R_UNEXPECTEDEND)
+ continue;
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+ keydata.refresh = refresh_time(kfetch, true);
+ set_refreshkeytimer(zone, &keydata, now, false);
+
+ dns_rdata_reset(&rdata);
+ isc_buffer_init(&keyb, key_buf, sizeof(key_buf));
+ CHECK(dns_rdata_fromstruct(&rdata,
+ zone->rdclass, dns_rdatatype_keydata,
+ &keydata, &keyb));
+
+ /* Insert updated version */
+ CHECK(update_one_rr(kfetch->db, ver, diff, DNS_DIFFOP_ADD,
+ name, 0, &rdata));
+ }
+ result = ISC_R_SUCCESS;
+ failure:
+ return (result);
+}
+
+/*
+ * Verify that DNSKEY set is signed by the key specified in 'keydata'.
+ */
+static bool
+revocable(dns_keyfetch_t *kfetch, dns_rdata_keydata_t *keydata) {
+ isc_result_t result;
+ dns_name_t *keyname;
+ isc_mem_t *mctx;
+ dns_rdata_t sigrr = DNS_RDATA_INIT;
+ dns_rdata_t rr = DNS_RDATA_INIT;
+ dns_rdata_rrsig_t sig;
+ dns_rdata_dnskey_t dnskey;
+ dst_key_t *dstkey = NULL;
+ unsigned char key_buf[4096];
+ isc_buffer_t keyb;
+ bool answer = false;
+
+ REQUIRE(kfetch != NULL && keydata != NULL);
+ REQUIRE(dns_rdataset_isassociated(&kfetch->dnskeysigset));
+
+ keyname = dns_fixedname_name(&kfetch->name);
+ mctx = kfetch->zone->view->mctx;
+
+ /* Generate a key from keydata */
+ isc_buffer_init(&keyb, key_buf, sizeof(key_buf));
+ dns_keydata_todnskey(keydata, &dnskey, NULL);
+ dns_rdata_fromstruct(&rr, keydata->common.rdclass,
+ dns_rdatatype_dnskey, &dnskey, &keyb);
+ result = dns_dnssec_keyfromrdata(keyname, &rr, mctx, &dstkey);
+ if (result != ISC_R_SUCCESS)
+ return (false);
+
+ /* See if that key generated any of the signatures */
+ for (result = dns_rdataset_first(&kfetch->dnskeysigset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&kfetch->dnskeysigset))
+ {
+ dns_fixedname_t fixed;
+ dns_fixedname_init(&fixed);
+
+ dns_rdata_reset(&sigrr);
+ dns_rdataset_current(&kfetch->dnskeysigset, &sigrr);
+ result = dns_rdata_tostruct(&sigrr, &sig, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ if (dst_key_alg(dstkey) == sig.algorithm &&
+ dst_key_rid(dstkey) == sig.keyid)
+ {
+ result = dns_dnssec_verify2(keyname,
+ &kfetch->dnskeyset,
+ dstkey, false, mctx, &sigrr,
+ dns_fixedname_name(&fixed));
+
+ dns_zone_log(kfetch->zone, ISC_LOG_DEBUG(3),
+ "Confirm revoked DNSKEY is self-signed: "
+ "%s", dns_result_totext(result));
+
+ if (result == ISC_R_SUCCESS) {
+ answer = true;
+ break;
+ }
+ }
+ }
+
+ dst_key_free(&dstkey);
+ return (answer);
+}
+
+/*
+ * A DNSKEY set has been fetched from the zone apex of a zone whose trust
+ * anchors are being managed; scan the keyset, and update the key zone and the
+ * local trust anchors according to RFC5011.
+ */
+static void
+keyfetch_done(isc_task_t *task, isc_event_t *event) {
+ isc_result_t result, eresult;
+ dns_fetchevent_t *devent;
+ dns_keyfetch_t *kfetch;
+ dns_zone_t *zone;
+ isc_mem_t *mctx = NULL;
+ dns_keytable_t *secroots = NULL;
+ dns_dbversion_t *ver = NULL;
+ dns_diff_t diff;
+ bool alldone = false;
+ bool commit = false;
+ dns_name_t *keyname;
+ dns_rdata_t sigrr = DNS_RDATA_INIT;
+ dns_rdata_t dnskeyrr = DNS_RDATA_INIT;
+ dns_rdata_t keydatarr = DNS_RDATA_INIT;
+ dns_rdata_rrsig_t sig;
+ dns_rdata_dnskey_t dnskey;
+ dns_rdata_keydata_t keydata;
+ bool initializing;
+ char namebuf[DNS_NAME_FORMATSIZE];
+ unsigned char key_buf[4096];
+ isc_buffer_t keyb;
+ dst_key_t *dstkey;
+ isc_stdtime_t now;
+ int pending = 0;
+ bool secure = false;
+ bool free_needed;
+
+ UNUSED(task);
+ INSIST(event != NULL && event->ev_type == DNS_EVENT_FETCHDONE);
+ INSIST(event->ev_arg != NULL);
+
+ kfetch = event->ev_arg;
+ zone = kfetch->zone;
+ isc_mem_attach(zone->mctx, &mctx);
+ keyname = dns_fixedname_name(&kfetch->name);
+
+ devent = (dns_fetchevent_t *) event;
+ 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);
+ isc_event_free(&event);
+ dns_resolver_destroyfetch(&kfetch->fetch);
+
+ LOCK_ZONE(zone);
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_EXITING) || zone->view == NULL)
+ goto cleanup;
+
+ isc_stdtime_get(&now);
+ dns_name_format(keyname, namebuf, sizeof(namebuf));
+
+ result = dns_view_getsecroots(zone->view, &secroots);
+ INSIST(result == ISC_R_SUCCESS);
+
+ dns_diff_init(mctx, &diff);
+
+ CHECK(dns_db_newversion(kfetch->db, &ver));
+
+ zone->refreshkeycount--;
+ alldone = (zone->refreshkeycount == 0);
+
+ if (alldone)
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_REFRESHING);
+
+ dns_zone_log(zone, ISC_LOG_DEBUG(3),
+ "Returned from key fetch in keyfetch_done() for "
+ "'%s': %s", namebuf, dns_result_totext(eresult));
+
+ /* Fetch failed */
+ if (eresult != ISC_R_SUCCESS ||
+ !dns_rdataset_isassociated(&kfetch->dnskeyset)) {
+ dns_zone_log(zone, ISC_LOG_WARNING,
+ "Unable to fetch DNSKEY set "
+ "'%s': %s", namebuf, dns_result_totext(eresult));
+ CHECK(minimal_update(kfetch, ver, &diff));
+ goto done;
+ }
+
+ /* No RRSIGs found */
+ if (!dns_rdataset_isassociated(&kfetch->dnskeysigset)) {
+ dns_zone_log(zone, ISC_LOG_WARNING,
+ "No DNSKEY RRSIGs found for "
+ "'%s': %s", namebuf, dns_result_totext(eresult));
+ CHECK(minimal_update(kfetch, ver, &diff));
+ goto done;
+ }
+
+ /*
+ * Clear any cached trust level, as we need to run validation
+ * over again; trusted keys might have changed.
+ */
+ kfetch->dnskeyset.trust = kfetch->dnskeysigset.trust = dns_trust_none;
+
+ /*
+ * Validate the dnskeyset against the current trusted keys.
+ */
+ for (result = dns_rdataset_first(&kfetch->dnskeysigset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&kfetch->dnskeysigset)) {
+ dns_keynode_t *keynode = NULL;
+
+ dns_rdata_reset(&sigrr);
+ dns_rdataset_current(&kfetch->dnskeysigset, &sigrr);
+ result = dns_rdata_tostruct(&sigrr, &sig, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ result = dns_keytable_find(secroots, keyname, &keynode);
+ while (result == ISC_R_SUCCESS) {
+ dns_keynode_t *nextnode = NULL;
+ dns_fixedname_t fixed;
+ dns_fixedname_init(&fixed);
+
+ dstkey = dns_keynode_key(keynode);
+ if (dstkey == NULL) /* fail_secure() was called */
+ break;
+
+ if (dst_key_alg(dstkey) == sig.algorithm &&
+ dst_key_id(dstkey) == sig.keyid) {
+ result = dns_dnssec_verify2(keyname,
+ &kfetch->dnskeyset,
+ dstkey, false,
+ zone->view->mctx, &sigrr,
+ dns_fixedname_name(&fixed));
+
+ dns_zone_log(zone, ISC_LOG_DEBUG(3),
+ "Verifying DNSKEY set for zone "
+ "'%s' using key %d/%d: %s",
+ namebuf, sig.keyid, sig.algorithm,
+ dns_result_totext(result));
+
+ if (result == ISC_R_SUCCESS) {
+ kfetch->dnskeyset.trust =
+ dns_trust_secure;
+ kfetch->dnskeysigset.trust =
+ dns_trust_secure;
+ break;
+ }
+ }
+
+ result = dns_keytable_nextkeynode(secroots,
+ keynode, &nextnode);
+ dns_keytable_detachkeynode(secroots, &keynode);
+ keynode = nextnode;
+ }
+
+ if (keynode != NULL)
+ dns_keytable_detachkeynode(secroots, &keynode);
+
+ if (kfetch->dnskeyset.trust == dns_trust_secure) {
+ secure = true;
+ break;
+ }
+ }
+
+ /*
+ * If we were not able to verify the answer using the current
+ * trusted keys then all we can do is look at any revoked keys.
+ */
+
+ if (!secure) {
+ dns_zone_log(zone, ISC_LOG_DEBUG(3),
+ "DNSKEY set for zone '%s' could not be verified "
+ "with current keys", namebuf);
+ }
+
+ /*
+ * First scan keydataset to find keys that are not in dnskeyset
+ * - Missing keys which are not scheduled for removal,
+ * log a warning
+ * - Missing keys which are scheduled for removal and
+ * the remove hold-down timer has completed should
+ * be removed from the key zone
+ * - Missing keys whose acceptance timers have not yet
+ * completed, log a warning and reset the acceptance
+ * timer to 30 days in the future
+ * - All keys not being removed have their refresh timers
+ * updated
+ */
+ initializing = true;
+ for (result = dns_rdataset_first(&kfetch->keydataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&kfetch->keydataset))
+ {
+ dns_keytag_t keytag;
+
+ dns_rdata_reset(&keydatarr);
+ dns_rdataset_current(&kfetch->keydataset, &keydatarr);
+ result = dns_rdata_tostruct(&keydatarr, &keydata, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ dns_keydata_todnskey(&keydata, &dnskey, NULL);
+ result = compute_tag(keyname, &dnskey, mctx, &keytag);
+ if (result != ISC_R_SUCCESS) {
+ /*
+ * Skip if we cannot compute the key tag.
+ * This may happen if the algorithm is unsupported
+ */
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "Cannot compute tag for key in zone %s: %s "
+ "(skipping)",
+ namebuf, dns_result_totext(result));
+ continue;
+ }
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ /*
+ * If any keydata record has a nonzero add holddown, then
+ * there was a pre-existing trust anchor for this domain;
+ * that means we are *not* initializing it and shouldn't
+ * automatically trust all the keys we find at the zone apex.
+ */
+ initializing = initializing && (keydata.addhd == 0);
+
+ if (! matchkey(&kfetch->dnskeyset, &keydatarr)) {
+ bool deletekey = false;
+
+ if (!secure) {
+ if (keydata.removehd != 0 &&
+ keydata.removehd <= now) {
+ deletekey = true;
+ }
+ } else if (keydata.addhd == 0) {
+ deletekey = true;
+ } else if (keydata.addhd > now) {
+ dns_zone_log(zone, ISC_LOG_DEBUG(3),
+ "Pending key %d for zone %s "
+ "unexpectedly missing "
+ "restarting 30-day acceptance "
+ "timer", keytag, namebuf);
+ if (keydata.addhd < now + dns_zone_mkey_month)
+ keydata.addhd =
+ now + dns_zone_mkey_month;
+ keydata.refresh = refresh_time(kfetch,
+ false);
+ } else if (keydata.removehd == 0) {
+ dns_zone_log(zone, ISC_LOG_DEBUG(3),
+ "Active key %d for zone %s "
+ "unexpectedly missing",
+ keytag, namebuf);
+ keydata.refresh = now + dns_zone_mkey_hour;
+ } else if (keydata.removehd <= now) {
+ deletekey = true;
+ dns_zone_log(zone, ISC_LOG_DEBUG(3),
+ "Revoked key %d for zone %s "
+ "missing: deleting from "
+ "managed keys database",
+ keytag, namebuf);
+ } else {
+ keydata.refresh = refresh_time(kfetch,
+ false);
+ }
+
+ if (secure || deletekey) {
+ /* Delete old version */
+ CHECK(update_one_rr(kfetch->db, ver, &diff,
+ DNS_DIFFOP_DEL, keyname, 0,
+ &keydatarr));
+ }
+
+ if (!secure || deletekey)
+ continue;
+
+ dns_rdata_reset(&keydatarr);
+ isc_buffer_init(&keyb, key_buf, sizeof(key_buf));
+ dns_rdata_fromstruct(&keydatarr, zone->rdclass,
+ dns_rdatatype_keydata,
+ &keydata, &keyb);
+
+ /* Insert updated version */
+ CHECK(update_one_rr(kfetch->db, ver, &diff,
+ DNS_DIFFOP_ADD, keyname, 0,
+ &keydatarr));
+
+ set_refreshkeytimer(zone, &keydata, now, false);
+ }
+ }
+
+ /*
+ * Next scan dnskeyset:
+ * - If new keys are found (i.e., lacking a match in keydataset)
+ * add them to the key zone and set the acceptance timer
+ * to 30 days in the future (or to immediately if we've
+ * determined that we're initializing the zone for the
+ * first time)
+ * - Previously-known keys that have been revoked
+ * must be scheduled for removal from the key zone (or,
+ * if they hadn't been accepted as trust anchors yet
+ * anyway, removed at once)
+ * - Previously-known unrevoked keys whose acceptance timers
+ * have completed are promoted to trust anchors
+ * - All keys not being removed have their refresh
+ * timers updated
+ */
+ for (result = dns_rdataset_first(&kfetch->dnskeyset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&kfetch->dnskeyset))
+ {
+ bool revoked = false;
+ bool newkey = false;
+ bool updatekey = false;
+ bool deletekey = false;
+ bool trustkey = false;
+ dns_keytag_t keytag;
+
+ dns_rdata_reset(&dnskeyrr);
+ dns_rdataset_current(&kfetch->dnskeyset, &dnskeyrr);
+ result = dns_rdata_tostruct(&dnskeyrr, &dnskey, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ /* Skip ZSK's */
+ if (!(dnskey.flags & DNS_KEYFLAG_KSK))
+ continue;
+
+ result = compute_tag(keyname, &dnskey, mctx, &keytag);
+ if (result != ISC_R_SUCCESS) {
+ /*
+ * Skip if we cannot compute the key tag.
+ * This may happen if the algorithm is unsupported
+ */
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "Cannot compute tag for key in zone %s: %s "
+ "(skipping)",
+ namebuf, dns_result_totext(result));
+ continue;
+ }
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ revoked = (dnskey.flags & DNS_KEYFLAG_REVOKE);
+
+ if (matchkey(&kfetch->keydataset, &dnskeyrr)) {
+ dns_rdata_reset(&keydatarr);
+ dns_rdataset_current(&kfetch->keydataset, &keydatarr);
+ result = dns_rdata_tostruct(&keydatarr, &keydata, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ if (revoked && revocable(kfetch, &keydata)) {
+ if (keydata.addhd > now) {
+ /*
+ * Key wasn't trusted yet, and now
+ * it's been revoked? Just remove it
+ */
+ deletekey = true;
+ dns_zone_log(zone, ISC_LOG_DEBUG(3),
+ "Pending key %d "
+ "for zone %s is now "
+ "revoked: "
+ "deleting from the "
+ "managed keys database",
+ keytag, namebuf);
+ } else if (keydata.removehd == 0) {
+ /*
+ * Remove key from secroots.
+ */
+ dns_view_untrust(zone->view, keyname,
+ &dnskey, mctx);
+
+ /* If initializing, delete now */
+ if (keydata.addhd == 0)
+ deletekey = true;
+ else {
+ keydata.removehd = now +
+ dns_zone_mkey_month;
+ keydata.flags |=
+ DNS_KEYFLAG_REVOKE;
+ }
+
+ dns_zone_log(zone, ISC_LOG_INFO,
+ "Trusted key %d "
+ "for zone %s is now "
+ "revoked",
+ keytag, namebuf);
+ } else if (keydata.removehd < now) {
+ /* Scheduled for removal */
+ deletekey = true;
+
+ dns_zone_log(zone, ISC_LOG_INFO,
+ "Revoked key %d "
+ "for zone %s removal "
+ "timer complete: "
+ "deleting from the "
+ "managed keys database",
+ keytag, namebuf);
+ }
+ } else if (revoked && keydata.removehd == 0) {
+ dns_zone_log(zone, ISC_LOG_WARNING,
+ "Active key %d for zone "
+ "%s is revoked but "
+ "did not self-sign; "
+ "ignoring", keytag, namebuf);
+ continue;
+ } else if (secure) {
+ if (keydata.removehd != 0) {
+ /*
+ * Key isn't revoked--but it
+ * seems it used to be.
+ * Remove it now and add it
+ * back as if it were a fresh key,
+ * with a 30-day acceptance timer.
+ */
+ deletekey = true;
+ newkey = true;
+ keydata.removehd = 0;
+ keydata.addhd =
+ now + dns_zone_mkey_month;
+
+ dns_zone_log(zone, ISC_LOG_DEBUG(3),
+ "Revoked key %d "
+ "for zone %s "
+ "has returned: starting "
+ "30-day acceptance timer",
+ keytag, namebuf);
+ } else if (keydata.addhd > now)
+ pending++;
+ else if (keydata.addhd == 0)
+ keydata.addhd = now;
+
+ if (keydata.addhd <= now) {
+ trustkey = true;
+ dns_zone_log(zone, ISC_LOG_INFO,
+ "Key %d for zone %s "
+ "acceptance timer "
+ "complete: "
+ "key now trusted",
+ keytag, namebuf);
+ }
+ } else if (keydata.addhd > now) {
+ /*
+ * Not secure, and key is pending:
+ * reset the acceptance timer
+ */
+ pending++;
+ keydata.addhd = now + dns_zone_mkey_month;
+ dns_zone_log(zone, ISC_LOG_DEBUG(3),
+ "Pending key %d "
+ "for zone %s was "
+ "not validated: restarting "
+ "30-day acceptance timer",
+ keytag, namebuf);
+ }
+
+ if (!deletekey && !newkey)
+ updatekey = true;
+ } else if (secure) {
+ /*
+ * Key wasn't in the key zone but it's
+ * revoked now anyway, so just skip it
+ */
+ if (revoked)
+ continue;
+
+ /* Key wasn't in the key zone: add it */
+ newkey = true;
+
+ if (initializing) {
+ dns_zone_log(zone, ISC_LOG_WARNING,
+ "Initializing automatic trust "
+ "anchor management for zone '%s'; "
+ "DNSKEY ID %d is now trusted, "
+ "waiving the normal 30-day "
+ "waiting period.",
+ namebuf, keytag);
+ trustkey = true;
+ } else {
+ dns_zone_log(zone, ISC_LOG_INFO,
+ "New key %d observed "
+ "for zone '%s': "
+ "starting 30-day "
+ "acceptance timer",
+ keytag, namebuf);
+ }
+ } else {
+ /*
+ * No previously known key, and the key is not
+ * secure, so skip it.
+ */
+ continue;
+ }
+
+ /* Delete old version */
+ if (deletekey || !newkey)
+ CHECK(update_one_rr(kfetch->db, ver, &diff,
+ DNS_DIFFOP_DEL, keyname, 0,
+ &keydatarr));
+
+ if (updatekey) {
+ /* Set refresh timer */
+ keydata.refresh = refresh_time(kfetch, false);
+ dns_rdata_reset(&keydatarr);
+ isc_buffer_init(&keyb, key_buf, sizeof(key_buf));
+ dns_rdata_fromstruct(&keydatarr, zone->rdclass,
+ dns_rdatatype_keydata,
+ &keydata, &keyb);
+
+ /* Insert updated version */
+ CHECK(update_one_rr(kfetch->db, ver, &diff,
+ DNS_DIFFOP_ADD, keyname, 0,
+ &keydatarr));
+ } else if (newkey) {
+ /* Convert DNSKEY to KEYDATA */
+ result = dns_rdata_tostruct(&dnskeyrr, &dnskey, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ dns_keydata_fromdnskey(&keydata, &dnskey, 0, 0, 0,
+ NULL);
+ keydata.addhd = initializing
+ ? now : now + dns_zone_mkey_month;
+ keydata.refresh = refresh_time(kfetch, false);
+ dns_rdata_reset(&keydatarr);
+ isc_buffer_init(&keyb, key_buf, sizeof(key_buf));
+ dns_rdata_fromstruct(&keydatarr, zone->rdclass,
+ dns_rdatatype_keydata,
+ &keydata, &keyb);
+
+ /* Insert into key zone */
+ CHECK(update_one_rr(kfetch->db, ver, &diff,
+ DNS_DIFFOP_ADD, keyname, 0,
+ &keydatarr));
+ }
+
+ if (trustkey) {
+ /* Trust this key. */
+ result = dns_rdata_tostruct(&dnskeyrr, &dnskey, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ trust_key(zone, keyname, &dnskey, mctx);
+ }
+
+ if (secure && !deletekey) {
+ INSIST(newkey || updatekey);
+ set_refreshkeytimer(zone, &keydata, now, false);
+ }
+ }
+
+ /*
+ * RFC5011 says, "A trust point that has all of its trust anchors
+ * revoked is considered deleted and is treated as if the trust
+ * point was never configured." But if someone revoked their
+ * active key before the standby was trusted, that would mean the
+ * zone would suddenly be nonsecured. We avoid this by checking to
+ * see if there's pending keydata. If so, we put a null key in
+ * the security roots; then all queries to the zone will fail.
+ */
+ if (pending != 0)
+ fail_secure(zone, keyname);
+
+ done:
+
+ if (!ISC_LIST_EMPTY(diff.tuples)) {
+ /* Write changes to journal file. */
+ CHECK(update_soa_serial(kfetch->db, ver, &diff, mctx,
+ zone->updatemethod));
+ CHECK(zone_journal(zone, &diff, NULL, "keyfetch_done"));
+ commit = true;
+
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED);
+ zone_needdump(zone, 30);
+ } else if (result == ISC_R_NOMORE) {
+ /*
+ * If "updatekey" was true for all keys found in the DNSKEY
+ * response and the previous update of those keys happened
+ * during the same second (only possible if a key refresh was
+ * externally triggered), it may happen that all relevant
+ * update_one_rr() calls will return ISC_R_SUCCESS, but
+ * diff.tuples will remain empty. Reset result to
+ * ISC_R_SUCCESS to prevent a bogus warning from being logged.
+ */
+ result = ISC_R_SUCCESS;
+ }
+
+ failure:
+
+ dns_diff_clear(&diff);
+ if (ver != NULL)
+ dns_db_closeversion(kfetch->db, &ver, commit);
+
+ cleanup:
+ dns_db_detach(&kfetch->db);
+
+ INSIST(zone->irefs > 0);
+ zone->irefs--;
+ kfetch->zone = NULL;
+
+ if (dns_rdataset_isassociated(&kfetch->keydataset))
+ dns_rdataset_disassociate(&kfetch->keydataset);
+ if (dns_rdataset_isassociated(&kfetch->dnskeyset))
+ dns_rdataset_disassociate(&kfetch->dnskeyset);
+ if (dns_rdataset_isassociated(&kfetch->dnskeysigset))
+ dns_rdataset_disassociate(&kfetch->dnskeysigset);
+
+ dns_name_free(keyname, mctx);
+ isc_mem_put(mctx, kfetch, sizeof(dns_keyfetch_t));
+ isc_mem_detach(&mctx);
+
+ if (secroots != NULL)
+ dns_keytable_detach(&secroots);
+
+ free_needed = exit_check(zone);
+ UNLOCK_ZONE(zone);
+ if (free_needed)
+ zone_free(zone);
+
+ INSIST(ver == NULL);
+}
+
+/*
+ * Refresh the data in the key zone. Initiate a fetch to get new DNSKEY
+ * records from the zone apex.
+ */
+static void
+zone_refreshkeys(dns_zone_t *zone) {
+ const char me[] = "zone_refreshkeys";
+ isc_result_t result;
+ dns_rriterator_t rrit;
+ dns_db_t *db = NULL;
+ dns_dbversion_t *ver = NULL;
+ dns_diff_t diff;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_keydata_t kd;
+ isc_stdtime_t now;
+ bool commit = false;
+ bool fetching = false, fetch_err = false;
+
+ ENTER;
+ REQUIRE(zone->db != NULL);
+
+ isc_stdtime_get(&now);
+
+ LOCK_ZONE(zone);
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_EXITING)) {
+ isc_time_settoepoch(&zone->refreshkeytime);
+ UNLOCK_ZONE(zone);
+ return;
+ }
+
+ ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read);
+ dns_db_attach(zone->db, &db);
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
+
+ dns_diff_init(zone->mctx, &diff);
+
+ CHECK(dns_db_newversion(db, &ver));
+
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_REFRESHING);
+
+ dns_rriterator_init(&rrit, db, ver, 0);
+ for (result = dns_rriterator_first(&rrit);
+ result == ISC_R_SUCCESS;
+ result = dns_rriterator_nextrrset(&rrit)) {
+ isc_stdtime_t timer = 0xffffffff;
+ dns_name_t *name = NULL, *kname = NULL;
+ dns_rdataset_t *kdset = NULL;
+ dns_keyfetch_t *kfetch;
+ uint32_t ttl;
+
+ dns_rriterator_current(&rrit, &name, &ttl, &kdset, NULL);
+ if (kdset == NULL || kdset->type != dns_rdatatype_keydata ||
+ !dns_rdataset_isassociated(kdset))
+ continue;
+
+ /*
+ * Scan the stored keys looking for ones that need
+ * removal or refreshing
+ */
+ for (result = dns_rdataset_first(kdset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(kdset)) {
+ dns_rdata_reset(&rdata);
+ dns_rdataset_current(kdset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &kd, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ /* Removal timer expired? */
+ if (kd.removehd != 0 && kd.removehd < now) {
+ CHECK(update_one_rr(db, ver, &diff,
+ DNS_DIFFOP_DEL, name, ttl,
+ &rdata));
+ continue;
+ }
+
+ /* Acceptance timer expired? */
+ if (kd.addhd <= now)
+ timer = kd.addhd;
+
+ /* Or do we just need to refresh the keyset? */
+ if (timer > kd.refresh)
+ timer = kd.refresh;
+ }
+
+ if (timer > now)
+ continue;
+
+ kfetch = isc_mem_get(zone->mctx, sizeof(dns_keyfetch_t));
+ if (kfetch == NULL) {
+ fetch_err = true;
+ goto failure;
+ }
+
+ zone->refreshkeycount++;
+ kfetch->zone = zone;
+ zone->irefs++;
+ INSIST(zone->irefs != 0);
+ kname = dns_fixedname_initname(&kfetch->name);
+ dns_name_dup(name, zone->mctx, kname);
+ dns_rdataset_init(&kfetch->dnskeyset);
+ dns_rdataset_init(&kfetch->dnskeysigset);
+ dns_rdataset_init(&kfetch->keydataset);
+ dns_rdataset_clone(kdset, &kfetch->keydataset);
+ kfetch->db = NULL;
+ dns_db_attach(db, &kfetch->db);
+ kfetch->fetch = NULL;
+
+ if (isc_log_wouldlog(dns_lctx, ISC_LOG_DEBUG(3))) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ dns_name_format(kname, namebuf,
+ sizeof(namebuf));
+ dns_zone_log(zone, ISC_LOG_DEBUG(3),
+ "Creating key fetch in "
+ "zone_refreshkeys() for '%s'",
+ namebuf);
+ }
+
+ /*
+ * Use of DNS_FETCHOPT_NOCACHED is essential here. If it is
+ * not set and the cache still holds a non-expired, validated
+ * version of the RRset being queried for by the time the
+ * response is received, the cached RRset will be passed to
+ * keyfetch_done() instead of the one received in the response
+ * as the latter will have a lower trust level due to not being
+ * validated until keyfetch_done() is called.
+ */
+
+#ifdef ENABLE_AFL
+ if (dns_fuzzing_resolver == false) {
+#endif
+ result = dns_resolver_createfetch(zone->view->resolver,
+ kname, dns_rdatatype_dnskey,
+ NULL, NULL, NULL,
+ DNS_FETCHOPT_NOVALIDATE|
+ DNS_FETCHOPT_UNSHARED|
+ DNS_FETCHOPT_NOCACHED,
+ zone->task,
+ keyfetch_done, kfetch,
+ &kfetch->dnskeyset,
+ &kfetch->dnskeysigset,
+ &kfetch->fetch);
+#ifdef ENABLE_AFL
+ } else {
+ result = ISC_R_FAILURE;
+ }
+#endif
+ if (result == ISC_R_SUCCESS)
+ fetching = true;
+ else {
+ zone->refreshkeycount--;
+ zone->irefs--;
+ dns_db_detach(&kfetch->db);
+ dns_rdataset_disassociate(&kfetch->keydataset);
+ dns_name_free(kname, zone->mctx);
+ isc_mem_put(zone->mctx, kfetch, sizeof(dns_keyfetch_t));
+ dns_zone_log(zone, ISC_LOG_WARNING,
+ "Failed to create fetch for "
+ "DNSKEY update");
+ fetch_err = true;
+ }
+ }
+ if (!ISC_LIST_EMPTY(diff.tuples)) {
+ CHECK(update_soa_serial(db, ver, &diff, zone->mctx,
+ zone->updatemethod));
+ CHECK(zone_journal(zone, &diff, NULL, "zone_refreshkeys"));
+ commit = true;
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED);
+ zone_needdump(zone, 30);
+ }
+
+ failure:
+ if (fetch_err) {
+ /*
+ * Error during a key fetch; retry in an hour.
+ */
+ isc_time_t timenow, timethen;
+ char timebuf[80];
+
+ TIME_NOW(&timenow);
+ DNS_ZONE_TIME_ADD(&timenow, dns_zone_mkey_hour, &timethen);
+ zone->refreshkeytime = timethen;
+ zone_settimer(zone, &timenow);
+
+ isc_time_formattimestamp(&zone->refreshkeytime, timebuf, 80);
+ dns_zone_log(zone, ISC_LOG_DEBUG(1), "retry key refresh: %s",
+ timebuf);
+ }
+
+ if (!fetching)
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_REFRESHING);
+
+ dns_diff_clear(&diff);
+ if (ver != NULL) {
+ dns_rriterator_destroy(&rrit);
+ dns_db_closeversion(db, &ver, commit);
+ }
+ dns_db_detach(&db);
+
+ UNLOCK_ZONE(zone);
+
+ INSIST(ver == NULL);
+}
+
+static void
+zone_maintenance(dns_zone_t *zone) {
+ const char me[] = "zone_maintenance";
+ isc_time_t now;
+ isc_result_t result;
+ bool dumping, load_pending;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+ ENTER;
+
+ /*
+ * Are we pending load/reload?
+ */
+ LOCK_ZONE(zone);
+ load_pending = DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADPENDING);
+ UNLOCK_ZONE(zone);
+
+ if (load_pending) {
+ return;
+ }
+
+ /*
+ * 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 will be NULL, and we had better not try
+ * to do further maintenance on it.
+ */
+ if (zone->view == NULL || zone->view->adb == NULL)
+ return;
+
+ TIME_NOW(&now);
+
+ /*
+ * Expire check.
+ */
+ switch (zone->type) {
+ case dns_zone_redirect:
+ if (zone->masters == NULL)
+ break;
+ /* FALLTHROUGH */
+ 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_redirect:
+ if (zone->masters == NULL)
+ break;
+ /* FALLTHROUGH */
+ 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;
+ }
+
+ /*
+ * Slaves send notifies before backing up to disk, masters after.
+ */
+ if (zone->type == dns_zone_slave &&
+ (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDNOTIFY) ||
+ DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDSTARTUPNOTIFY)) &&
+ isc_time_compare(&now, &zone->notifytime) >= 0)
+ zone_notify(zone, &now);
+
+ /*
+ * Do we need to consolidate the backing store?
+ */
+ switch (zone->type) {
+ case dns_zone_master:
+ case dns_zone_slave:
+ case dns_zone_key:
+ case dns_zone_redirect:
+ case dns_zone_stub:
+ 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 = true;
+ UNLOCK_ZONE(zone);
+ if (!dumping) {
+ result = zone_dump(zone, 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;
+ }
+
+ /*
+ * Master/redirect zones send notifies now, if needed
+ */
+ switch (zone->type) {
+ case dns_zone_master:
+ case dns_zone_redirect:
+ if ((DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDNOTIFY) ||
+ DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDSTARTUPNOTIFY))&&
+ isc_time_compare(&now, &zone->notifytime) >= 0)
+ zone_notify(zone, &now);
+ default:
+ break;
+ }
+
+ /*
+ * Do we need to refresh keys?
+ */
+ switch (zone->type) {
+ case dns_zone_key:
+ if (isc_time_compare(&now, &zone->refreshkeytime) >= 0) {
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADED) &&
+ !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_REFRESHING)) {
+ zone_refreshkeys(zone);
+ }
+ }
+ break;
+ case dns_zone_master:
+ if (!isc_time_isepoch(&zone->refreshkeytime) &&
+ isc_time_compare(&now, &zone->refreshkeytime) >= 0 &&
+ zone->rss_event == NULL)
+ zone_rekey(zone);
+ default:
+ break;
+ }
+
+ switch (zone->type) {
+ case dns_zone_master:
+ case dns_zone_redirect:
+ case dns_zone_slave:
+ /*
+ * Do we need to sign/resign some RRsets?
+ */
+ if (zone->rss_event != NULL)
+ break;
+ 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) {
+ uint32_t serial;
+ isc_result_t result = ISC_R_SUCCESS;
+ dns_zone_t *secure = NULL;
+
+ /*
+ * Obtaining a lock on the zone->secure (see zone_send_secureserial)
+ * could result in a deadlock due to a LOR so we will spin if we
+ * can't obtain the both locks.
+ */
+ again:
+ LOCK_ZONE(zone);
+ if (zone->type == dns_zone_master) {
+ if (inline_raw(zone)) {
+ unsigned int soacount;
+ secure = zone->secure;
+ INSIST(secure != zone);
+ TRYLOCK_ZONE(result, secure);
+ if (result != ISC_R_SUCCESS) {
+ UNLOCK_ZONE(zone);
+ secure = NULL;
+#ifdef ISC_PLATFORM_USETHREADS
+ isc_thread_yield();
+#endif
+ goto again;
+ }
+
+ ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read);
+ if (zone->db != NULL) {
+ result = zone_get_from_db(zone, zone->db, NULL,
+ &soacount, &serial,
+ NULL, NULL, NULL,
+ NULL, NULL);
+ } else
+ result = DNS_R_NOTLOADED;
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
+ if (result == ISC_R_SUCCESS && soacount > 0U)
+ zone_send_secureserial(zone, serial);
+ }
+
+ /* XXXMPA make separate call back */
+ if (result == ISC_R_SUCCESS)
+ set_resigntime(zone);
+ }
+ if (secure != NULL)
+ UNLOCK_ZONE(secure);
+ 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;
+ 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] = 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;
+ bool 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 = true;
+ UNLOCK_ZONE(zone);
+ if (!dumping)
+ result = zone_dump(zone, false); /* Unknown task. */
+ return (result);
+}
+
+isc_result_t
+dns_zone_dump(dns_zone_t *zone) {
+ isc_result_t result = ISC_R_ALREADYRUNNING;
+ bool dumping;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ dumping = was_dumping(zone);
+ UNLOCK_ZONE(zone);
+ if (!dumping)
+ result = zone_dump(zone, false); /* Unknown task. */
+ return (result);
+}
+
+static void
+zone_needdump(dns_zone_t *zone, unsigned int delay) {
+ const char me[] = "zone_needdump";
+ isc_time_t dumptime;
+ isc_time_t now;
+
+ /*
+ * 'zone' locked by caller
+ */
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(LOCKED_ZONE(zone));
+ ENTER;
+
+ /*
+ * 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_zone_t *secure = NULL;
+ dns_db_t *db;
+ dns_dbversion_t *version;
+ bool again = false;
+ bool compact = false;
+ 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);
+
+ /*
+ * Handle lock order inversion.
+ */
+ again:
+ LOCK_ZONE(zone);
+ if (inline_raw(zone)) {
+ secure = zone->secure;
+ INSIST(secure != zone);
+ TRYLOCK_ZONE(result, secure);
+ if (result != ISC_R_SUCCESS) {
+ UNLOCK_ZONE(zone);
+ secure = NULL;
+#if ISC_PLATFORM_USETHREADS
+ isc_thread_yield();
+#endif
+ goto again;
+ }
+ }
+
+ /*
+ * If there is a secure version of this zone
+ * use its serial if it is less than ours.
+ */
+ if (tresult == ISC_R_SUCCESS && secure != NULL) {
+ uint32_t sserial;
+ isc_result_t mresult;
+
+ ZONEDB_LOCK(&secure->dblock, isc_rwlocktype_read);
+ if (secure->db != NULL) {
+ mresult = dns_db_getsoaserial(zone->secure->db,
+ NULL, &sserial);
+ if (mresult == ISC_R_SUCCESS &&
+ isc_serial_lt(sserial, serial))
+ serial = sserial;
+ }
+ ZONEDB_UNLOCK(&secure->dblock, isc_rwlocktype_read);
+ }
+ if (secure != NULL)
+ UNLOCK_ZONE(secure);
+ UNLOCK_ZONE(zone);
+
+ /*
+ * 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 = 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 = 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, false);
+ dns_zone_idetach(&zone);
+}
+
+static isc_result_t
+zone_dump(dns_zone_t *zone, bool compact) {
+ const char me[] = "zone_dump";
+ isc_result_t result;
+ dns_dbversion_t *version = NULL;
+ bool 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 && zone->type != dns_zone_stub) {
+ dns_zone_t *dummy = NULL;
+ LOCK_ZONE(zone);
+ zone_iattach(zone, &dummy);
+ result = zonemgr_getio(zone->zmgr, 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 {
+ const dns_master_style_t *output_style;
+
+ dns_masterrawheader_t rawdata;
+ dns_db_currentversion(db, &version);
+ dns_master_initrawheader(&rawdata);
+ if (inline_secure(zone))
+ get_raw_serial(zone->raw, &rawdata);
+ if (zone->type == dns_zone_key)
+ output_style = &dns_master_style_keyzone;
+ else
+ output_style = &dns_master_style_default;
+ result = dns_master_dump3(zone->mctx, db, version,
+ output_style, masterfile,
+ masterformat, &rawdata);
+ dns_db_closeversion(db, &version, 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 = 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 = 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, const uint32_t rawversion)
+{
+ isc_result_t result;
+ dns_dbversion_t *version = NULL;
+ dns_db_t *db = NULL;
+ dns_masterrawheader_t rawdata;
+
+ 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);
+ dns_master_initrawheader(&rawdata);
+ if (rawversion == 0)
+ rawdata.flags |= DNS_MASTERRAW_COMPAT;
+ else if (inline_secure(zone))
+ get_raw_serial(zone->raw, &rawdata);
+ else if (zone->sourceserialset) {
+ rawdata.flags = DNS_MASTERRAW_SOURCESERIALSET;
+ rawdata.sourceserial = zone->sourceserial;
+ }
+ result = dns_master_dumptostream3(zone->mctx, db, version, style,
+ format, &rawdata, fd);
+ dns_db_closeversion(db, &version, false);
+ dns_db_detach(&db);
+ return (result);
+}
+
+isc_result_t
+dns_zone_dumptostream3(dns_zone_t *zone, FILE *fd, dns_masterformat_t format,
+ const dns_master_style_t *style,
+ const uint32_t rawversion)
+{
+ return (dumptostream(zone, fd, style, format, rawversion));
+}
+
+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, DNS_RAWFORMAT_VERSION));
+}
+
+isc_result_t
+dns_zone_dumptostream(dns_zone_t *zone, FILE *fd) {
+ return (dumptostream(zone, fd, &dns_master_style_default,
+ dns_masterformat_text, 0));
+}
+
+isc_result_t
+dns_zone_fulldumptostream(dns_zone_t *zone, FILE *fd) {
+ return (dumptostream(zone, fd, &dns_master_style_full,
+ dns_masterformat_text, 0));
+}
+
+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
+forward_cancel(dns_zone_t *zone) {
+ dns_forward_t *forward;
+
+ /*
+ * 'zone' locked by caller.
+ */
+
+ REQUIRE(LOCKED_ZONE(zone));
+
+ for (forward = ISC_LIST_HEAD(zone->forwards);
+ forward != NULL;
+ forward = ISC_LIST_NEXT(forward, link)) {
+ if (forward->request != NULL)
+ dns_request_cancel(forward->request);
+ }
+}
+
+static void
+zone_unload(dns_zone_t *zone) {
+ /*
+ * 'zone' locked by caller.
+ */
+
+ REQUIRE(LOCKED_ZONE(zone));
+
+ 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);
+ }
+ 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, uint32_t val) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(val > 0);
+
+ zone->minrefresh = val;
+}
+
+void
+dns_zone_setmaxrefreshtime(dns_zone_t *zone, uint32_t val) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(val > 0);
+
+ zone->maxrefresh = val;
+}
+
+void
+dns_zone_setminretrytime(dns_zone_t *zone, uint32_t val) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(val > 0);
+
+ zone->minretry = val;
+}
+
+void
+dns_zone_setmaxretrytime(dns_zone_t *zone, uint32_t val) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(val > 0);
+
+ zone->maxretry = val;
+}
+
+uint32_t
+dns_zone_getmaxrecords(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return (zone->maxrecords);
+}
+
+void
+dns_zone_setmaxrecords(dns_zone_t *zone, uint32_t val) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ zone->maxrecords = val;
+}
+
+static bool
+notify_isqueued(dns_zone_t *zone, unsigned int flags, dns_name_t *name,
+ isc_sockaddr_t *addr, dns_tsigkey_t *key)
+{
+ dns_notify_t *notify;
+ dns_zonemgr_t *zmgr;
+ isc_result_t result;
+
+ 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))
+ goto requeue;
+ if (addr != NULL && isc_sockaddr_equal(addr, &notify->dst) &&
+ notify->key == key)
+ goto requeue;
+ }
+ return (false);
+
+requeue:
+ /*
+ * If we are enqueued on the startup ratelimiter and this is
+ * not a startup notify, re-enqueue on the normal notify
+ * ratelimiter.
+ */
+ if (notify->event != NULL && (flags & DNS_NOTIFY_STARTUP) == 0 &&
+ (notify->flags & DNS_NOTIFY_STARTUP) != 0) {
+ zmgr = notify->zone->zmgr;
+ result = isc_ratelimiter_dequeue(zmgr->startupnotifyrl,
+ notify->event);
+ if (result != ISC_R_SUCCESS)
+ return (true);
+
+ notify->flags &= ~DNS_NOTIFY_STARTUP;
+ result = isc_ratelimiter_enqueue(notify->zone->zmgr->notifyrl,
+ notify->zone->task,
+ &notify->event);
+ if (result != ISC_R_SUCCESS) {
+ isc_event_free(&notify->event);
+ return (false);
+ }
+ }
+
+ return (true);
+}
+
+static bool
+notify_isself(dns_zone_t *zone, isc_sockaddr_t *dst) {
+ dns_tsigkey_t *key = NULL;
+ isc_sockaddr_t src;
+ isc_sockaddr_t any;
+ bool isself;
+ isc_netaddr_t dstaddr;
+ isc_result_t result;
+
+ if (zone->view == NULL || zone->isself == NULL)
+ return (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 (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);
+ result = dns_view_getpeertsig(zone->view, &dstaddr, &key);
+ if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND)
+ return (false);
+ 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, bool locked) {
+ isc_mem_t *mctx;
+
+ 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);
+ if (notify->key != NULL)
+ dns_tsigkey_detach(&notify->key);
+ 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;
+ notify->key = NULL;
+ notify->event = 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, 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, false);
+}
+
+
+static isc_result_t
+notify_send_queue(dns_notify_t *notify, bool startup) {
+ isc_event_t *e;
+ isc_result_t result;
+
+ INSIST(notify->event == NULL);
+ 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);
+ if (startup)
+ notify->event = e;
+ e->ev_arg = notify;
+ e->ev_sender = NULL;
+ result = isc_ratelimiter_enqueue(startup
+ ? notify->zone->zmgr->startupnotifyrl
+ : notify->zone->zmgr->notifyrl,
+ notify->zone->task, &e);
+ if (result != ISC_R_SUCCESS) {
+ isc_event_free(&e);
+ notify->event = NULL;
+ }
+ 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;
+ unsigned int options, timeout;
+ bool have_notifysource = false;
+ bool have_notifydscp = false;
+ isc_dscp_t dscp = -1;
+
+ notify = event->ev_arg;
+ REQUIRE(DNS_NOTIFY_VALID(notify));
+
+ UNUSED(task);
+
+ LOCK_ZONE(notify->zone);
+
+ notify->event = NULL;
+
+ 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_sockaddr_format(&notify->dst, addrbuf, sizeof(addrbuf));
+ if (notify->key != NULL) {
+ /* Transfer ownership of key */
+ key = notify->key;
+ notify->key = NULL;
+ } else {
+ isc_netaddr_fromsockaddr(&dstip, &notify->dst);
+ result = dns_view_getpeertsig(notify->zone->view, &dstip, &key);
+ if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) {
+ notify_log(notify->zone, ISC_LOG_ERROR,
+ "NOTIFY to %s not sent. "
+ "Peer TSIG key lookup failure.", addrbuf);
+ goto cleanup_message;
+ }
+ }
+
+ /* XXX: should we log the tsig key too? */
+ notify_log(notify->zone, ISC_LOG_DEBUG(3), "sending notify to %s",
+ addrbuf);
+ options = 0;
+ if (notify->zone->view->peers != NULL) {
+ dns_peer_t *peer = NULL;
+ bool usetcp = false;
+ 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 = true;
+ dns_peer_getnotifydscp(peer, &dscp);
+ if (dscp != -1)
+ have_notifydscp = true;
+ result = dns_peer_getforcetcp(peer, &usetcp);
+ if (result == ISC_R_SUCCESS && usetcp)
+ options |= DNS_FETCHOPT_TCP;
+ }
+ }
+ switch (isc_sockaddr_pf(&notify->dst)) {
+ case PF_INET:
+ if (!have_notifysource)
+ src = notify->zone->notifysrc4;
+ if (!have_notifydscp)
+ dscp = notify->zone->notifysrc4dscp;
+ break;
+ case PF_INET6:
+ if (!have_notifysource)
+ src = notify->zone->notifysrc6;
+ if (!have_notifydscp)
+ dscp = notify->zone->notifysrc6dscp;
+ 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_createvia4(notify->zone->view->requestmgr,
+ message, &src, &notify->dst, dscp,
+ options, key, timeout * 3, timeout,
+ 0, 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);
+ cleanup_message:
+ dns_message_destroy(&message);
+ cleanup:
+ UNLOCK_ZONE(notify->zone);
+ isc_event_free(&event);
+ if (result != ISC_R_SUCCESS)
+ notify_destroy(notify, false);
+}
+
+static void
+notify_send(dns_notify_t *notify) {
+ dns_adbaddrinfo_t *ai;
+ isc_sockaddr_t dst;
+ isc_result_t result;
+ dns_notify_t *newnotify = NULL;
+ unsigned int flags;
+ bool startup;
+
+ /*
+ * Zone lock held by caller.
+ */
+ REQUIRE(DNS_NOTIFY_VALID(notify));
+ REQUIRE(LOCKED_ZONE(notify->zone));
+
+ if (DNS_ZONE_FLAG(notify->zone, DNS_ZONEFLG_EXITING))
+ return;
+
+ for (ai = ISC_LIST_HEAD(notify->find->list);
+ ai != NULL;
+ ai = ISC_LIST_NEXT(ai, publink)) {
+ dst = ai->sockaddr;
+ if (notify_isqueued(notify->zone, notify->flags, NULL, &dst,
+ NULL))
+ continue;
+ if (notify_isself(notify->zone, &dst))
+ continue;
+ newnotify = NULL;
+ flags = notify->flags & DNS_NOTIFY_NOSOA;
+ result = notify_create(notify->mctx, flags, &newnotify);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ zone_iattach(notify->zone, &newnotify->zone);
+ ISC_LIST_APPEND(newnotify->zone->notifies, newnotify, link);
+ newnotify->dst = dst;
+ startup = (notify->flags & DNS_NOTIFY_STARTUP);
+ result = notify_send_queue(newnotify, startup);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ newnotify = NULL;
+ }
+
+ cleanup:
+ if (newnotify != NULL)
+ notify_destroy(newnotify, 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;
+ uint32_t serial;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdataset_t nsrdset;
+ dns_rdataset_t soardset;
+ isc_result_t result;
+ unsigned int i;
+ isc_sockaddr_t dst;
+ bool isqueued;
+ dns_notifytype_t notifytype;
+ unsigned int flags = 0;
+ bool loggednotify = false;
+ bool startup;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ startup = !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDNOTIFY);
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NEEDNOTIFY);
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NEEDSTARTUPNOTIFY);
+ notifytype = zone->notifytype;
+ DNS_ZONE_TIME_ADD(now, zone->notifydelay, &zone->notifytime);
+ UNLOCK_ZONE(zone);
+
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_EXITING) ||
+ ! 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;
+
+ /*
+ * Record that this was a notify due to starting up.
+ */
+ if (startup)
+ flags |= DNS_NOTIFY_STARTUP;
+
+ /*
+ * 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, 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++) {
+ dns_tsigkey_t *key = NULL;
+ dns_notify_t *notify = NULL;
+
+ if ((zone->notifykeynames != NULL) &&
+ (zone->notifykeynames[i] != NULL)) {
+ dns_view_t *view = dns_zone_getview(zone);
+ dns_name_t *keyname = zone->notifykeynames[i];
+ (void)dns_view_gettsig(view, keyname, &key);
+ }
+
+ dst = zone->notify[i];
+ if (notify_isqueued(zone, flags, NULL, &dst, key)) {
+ if (key != NULL)
+ dns_tsigkey_detach(&key);
+ continue;
+ }
+
+ result = notify_create(zone->mctx, flags, &notify);
+ if (result != ISC_R_SUCCESS) {
+ if (key != NULL)
+ dns_tsigkey_detach(&key);
+ continue;
+ }
+
+ zone_iattach(zone, &notify->zone);
+ notify->dst = dst;
+
+ INSIST(notify->key == NULL);
+
+ if (key != NULL) {
+ notify->key = key;
+ key = NULL;
+ }
+
+ ISC_LIST_APPEND(zone->notifies, notify, link);
+ result = notify_send_queue(notify, startup);
+ if (result != ISC_R_SUCCESS)
+ notify_destroy(notify, true);
+ if (!loggednotify) {
+ notify_log(zone, ISC_LOG_INFO,
+ "sending notifies (serial %u)",
+ serial);
+ loggednotify = true;
+ }
+ }
+ 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_notify_t *notify = NULL;
+
+ 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 explicitly
+ * 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 = true;
+ }
+
+ LOCK_ZONE(zone);
+ isqueued = notify_isqueued(zone, flags, &ns.name, NULL, 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, true);
+ UNLOCK_ZONE(zone);
+ continue;
+ }
+ LOCK_ZONE(zone);
+ ISC_LIST_APPEND(zone->notifies, notify, link);
+ UNLOCK_ZONE(zone);
+ notify_find_address(notify);
+ 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, 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, 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,
+ 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,
+ 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];
+ uint32_t nscnt, cnamecnt, refresh, retry, expire;
+ isc_result_t result;
+ isc_time_t now;
+ bool exiting = false;
+ isc_interval_t i;
+ unsigned int j, soacount;
+
+ stub = revent->ev_arg;
+ INSIST(DNS_STUB_VALID(stub));
+
+ UNUSED(task);
+
+ zone = stub->zone;
+
+ ENTER;
+
+ TIME_NOW(&now);
+
+ LOCK_ZONE(zone);
+
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_EXITING)) {
+ zone_debuglog(zone, me, 1, "exiting");
+ exiting = 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)) {
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NOEDNS);
+ 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);
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NOEDNS);
+ 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;
+ }
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_USEVC);
+ 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, true);
+ ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_write);
+ if (zone->db == NULL)
+ zone_attachdb(zone, stub->db);
+ result = zone_get_from_db(zone, zone->db, NULL, &soacount, NULL,
+ &refresh, &retry, &expire, NULL, NULL);
+ if (result == ISC_R_SUCCESS && soacount > 0U) {
+ 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);
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_HAVETIMERS);
+ }
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_write);
+ dns_db_detach(&stub->db);
+
+ dns_message_destroy(&msg);
+ isc_event_free(&event);
+ dns_request_destroy(&zone->request);
+
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_REFRESH);
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED);
+ 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);
+
+ if (zone->masterfile != NULL)
+ zone_needdump(zone, 0);
+
+ zone_settimer(zone, &now);
+ goto free_stub;
+
+ next_master:
+ if (stub->version != NULL)
+ dns_db_closeversion(stub->db, &stub->version, false);
+ if (stub->db != NULL)
+ dns_db_detach(&stub->db);
+ if (msg != NULL)
+ dns_message_destroy(&msg);
+ isc_event_free(&event);
+ 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) {
+ bool done = 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] == false) {
+ done = false;
+ break;
+ }
+ } else
+ done = 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);
+ goto free_stub;
+ }
+ }
+ queue_soa_query(zone);
+ goto free_stub;
+
+ same_master:
+ if (msg != NULL)
+ dns_message_destroy(&msg);
+ isc_event_free(&event);
+ dns_request_destroy(&zone->request);
+ ns_query(zone, NULL, stub);
+ UNLOCK_ZONE(zone);
+ goto done;
+
+ free_stub:
+ UNLOCK_ZONE(zone);
+ 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;
+}
+
+/*
+ * Get the EDNS EXPIRE option from the response and if it exists trim
+ * expire to be not more than it.
+ */
+static void
+get_edns_expire(dns_zone_t * zone, dns_message_t *message,
+ uint32_t *expirep)
+{
+ isc_result_t result;
+ uint32_t expire;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_buffer_t optbuf;
+ uint16_t optcode;
+ uint16_t optlen;
+
+ REQUIRE(expirep != NULL);
+ REQUIRE(message != NULL);
+
+ if (message->opt == NULL)
+ return;
+
+ result = dns_rdataset_first(message->opt);
+ if (result == ISC_R_SUCCESS) {
+ dns_rdataset_current(message->opt, &rdata);
+ isc_buffer_init(&optbuf, rdata.data, rdata.length);
+ isc_buffer_add(&optbuf, rdata.length);
+ while (isc_buffer_remaininglength(&optbuf) >= 4) {
+ optcode = isc_buffer_getuint16(&optbuf);
+ optlen = isc_buffer_getuint16(&optbuf);
+ /*
+ * A EDNS EXPIRE response has a length of 4.
+ */
+ if (optcode != DNS_OPT_EXPIRE || optlen != 4) {
+ isc_buffer_forward(&optbuf, optlen);
+ continue;
+ }
+ expire = isc_buffer_getuint32(&optbuf);
+ dns_zone_log(zone, ISC_LOG_DEBUG(1),
+ "got EDNS EXPIRE of %u", expire);
+ /*
+ * Trim *expirep?
+ */
+ if (expire < *expirep)
+ *expirep = expire;
+ break;
+ }
+ }
+}
+
+/*
+ * Set the file modification time zone->expire seconds before expiretime.
+ */
+static void
+setmodtime(dns_zone_t *zone, isc_time_t *expiretime) {
+ isc_result_t result;
+ isc_time_t when;
+ isc_interval_t i;
+
+ isc_interval_set(&i, zone->expire, 0);
+ result = isc_time_subtract(expiretime, &i, &when);
+ if (result != ISC_R_SUCCESS)
+ return;
+
+ result = ISC_R_FAILURE;
+ if (zone->journal != NULL)
+ result = isc_file_settime(zone->journal, &when);
+ if (result == ISC_R_SUCCESS &&
+ !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDDUMP) &&
+ !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDDUMP))
+ result = isc_file_settime(zone->masterfile, &when);
+ else if (result != ISC_R_SUCCESS)
+ result = isc_file_settime(zone->masterfile, &when);
+
+ /*
+ * Someone removed the file from underneath us!
+ */
+ if (result == ISC_R_FILENOTFOUND) {
+ zone_needdump(zone, DNS_DUMP_DELAY);
+ } 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));
+}
+
+/*
+ * 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;
+ 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;
+ uint32_t serial, oldserial = 0;
+ unsigned int j;
+ bool do_queue_xfrin = false;
+
+ zone = revent->ev_arg;
+ INSIST(DNS_ZONE_VALID(zone));
+
+ UNUSED(task);
+
+ ENTER;
+
+ TIME_NOW(&now);
+
+ LOCK_ZONE(zone);
+
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_EXITING)) {
+ isc_event_free(&event);
+ dns_request_destroy(&zone->request);
+ goto detach;
+ }
+
+ /*
+ * if timeout log and 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)) {
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NOEDNS);
+ 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 ||
+ zone->type == dns_zone_redirect) &&
+ DNS_ZONE_OPTION(zone, DNS_ZONEOPT_TRYTCPREFRESH)) {
+ if (!dns_zonemgr_unreachable(zone->zmgr,
+ &zone->masteraddr,
+ &zone->sourceaddr,
+ &now))
+ {
+ DNS_ZONE_SETFLAG(zone,
+ DNS_ZONEFLG_SOABEFOREAXFR);
+ 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);
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NOEDNS);
+ goto same_master;
+ }
+ if (!DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NOEDNS) &&
+ msg->rcode == dns_rcode_badvers) {
+ dns_zone_log(zone, ISC_LOG_DEBUG(1),
+ "refresh: rcode (%.*s) retrying without "
+ "EDNS EXPIRE OPTION master %s (source %s)",
+ (int)rb.used, rcode, master, source);
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NOEDNS);
+ 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 aren't.
+ */
+ if (msg->rcode == dns_rcode_refused &&
+ (zone->type == dns_zone_slave ||
+ zone->type == dns_zone_redirect))
+ 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 ||
+ zone->type == dns_zone_redirect) {
+ dns_zone_log(zone, ISC_LOG_INFO,
+ "refresh: truncated UDP answer, "
+ "initiating TCP zone xfer "
+ "for master %s (source %s)",
+ master, source);
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_SOABEFOREAXFR);
+ 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;
+ }
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_USEVC);
+ 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;
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADED)) {
+ unsigned int dbsoacount;
+ result = zone_get_from_db(zone, zone->db, NULL, &dbsoacount,
+ &oldserial, NULL, NULL, NULL, NULL,
+ NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ RUNTIME_CHECK(dbsoacount > 0U);
+ zone_debuglog(zone, me, 1, "serial: new %u, old %u",
+ serial, oldserial);
+ } else
+ zone_debuglog(zone, me, 1, "serial: new %u, old not loaded",
+ serial);
+
+ if (!DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADED) ||
+ DNS_ZONE_FLAG(zone, DNS_ZONEFLG_FORCEXFER) ||
+ isc_serial_gt(serial, oldserial)) {
+ 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->type == dns_zone_redirect) ?
+ "zone transfer" : "NS query",
+ master, source);
+ goto next_master;
+ }
+ tcp_transfer:
+ isc_event_free(&event);
+ dns_request_destroy(&zone->request);
+ if (zone->type == dns_zone_slave ||
+ zone->type == dns_zone_redirect) {
+ do_queue_xfrin = true;
+ } 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, oldserial)) {
+ isc_time_t expiretime;
+ uint32_t expire;
+
+ /*
+ * Compute the new expire time based on this response.
+ */
+ expire = zone->expire;
+ get_edns_expire(zone, msg, &expire);
+ DNS_ZONE_TIME_ADD(&now, expire, &expiretime);
+
+ /*
+ * Has the expire time improved?
+ */
+ if (isc_time_compare(&expiretime, &zone->expiretime) > 0) {
+ zone->expiretime = expiretime;
+ if (zone->masterfile != NULL)
+ setmodtime(zone, &expiretime);
+ }
+
+ DNS_ZONE_JITTER_ADD(&now, zone->refresh, &zone->refreshtime);
+ zone->mastersok[zone->curmaster] = 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, oldserial);
+ else
+ zone_debuglog(zone, me, 1, "ahead");
+ zone->mastersok[zone->curmaster] = 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);
+ 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) {
+ bool done = 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] == false) {
+ done = false;
+ break;
+ }
+ } else
+ done = 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);
+ goto detach;
+ }
+
+ requeue:
+ queue_soa_query(zone);
+ goto detach;
+
+ same_master:
+ if (msg != NULL)
+ dns_message_destroy(&msg);
+ isc_event_free(&event);
+ dns_request_destroy(&zone->request);
+ queue_soa_query(zone);
+
+ detach:
+ UNLOCK_ZONE(zone);
+ if (do_queue_xfrin)
+ queue_xfrin(zone);
+ 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->refreshrl, 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_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, uint16_t udpsize, bool reqnsid,
+ bool reqexpire)
+{
+ isc_result_t result;
+ dns_rdataset_t *rdataset = NULL;
+ dns_ednsopt_t ednsopts[DNS_EDNSOPTIONS];
+ int count = 0;
+
+ /* Set EDNS options if applicable */
+ if (reqnsid) {
+ INSIST(count < DNS_EDNSOPTIONS);
+ ednsopts[count].code = DNS_OPT_NSID;
+ ednsopts[count].length = 0;
+ ednsopts[count].value = NULL;
+ count++;
+ }
+ if (reqexpire) {
+ INSIST(count < DNS_EDNSOPTIONS);
+ ednsopts[count].code = DNS_OPT_EXPIRE;
+ ednsopts[count].length = 0;
+ ednsopts[count].value = NULL;
+ count++;
+ }
+ result = dns_message_buildopt(message, &rdataset, 0, udpsize, 0,
+ ednsopts, count);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ return (dns_message_setopt(message, rdataset));
+}
+
+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;
+ uint32_t options;
+ bool cancel = true;
+ int timeout;
+ bool have_xfrsource, have_xfrdscp, reqnsid, reqexpire;
+ uint16_t udpsize = SEND_BUFFER_SIZE;
+ isc_dscp_t dscp = -1;
+
+ 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 = false;
+ goto cleanup;
+ }
+
+ again:
+ result = create_query(zone, dns_rdatatype_soa, &message);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ 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);
+ goto skip_master;
+ }
+ }
+ if (key == NULL) {
+ result = dns_view_getpeertsig(zone->view, &masterip, &key);
+ if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) {
+ char addrbuf[ISC_NETADDR_FORMATSIZE];
+ isc_netaddr_format(&masterip, addrbuf, sizeof(addrbuf));
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "unable to find TSIG key for %s", addrbuf);
+ goto skip_master;
+ }
+ }
+
+ options = DNS_ZONE_FLAG(zone, DNS_ZONEFLG_USEVC) ?
+ DNS_REQUESTOPT_TCP : 0;
+ have_xfrsource = have_xfrdscp = false;
+ reqnsid = zone->view->requestnsid;
+ reqexpire = zone->requestexpire;
+ if (zone->view->peers != NULL) {
+ dns_peer_t *peer = NULL;
+ bool edns, usetcp;
+ 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 = true;
+ (void)dns_peer_gettransferdscp(peer, &dscp);
+ if (dscp != -1)
+ have_xfrdscp = 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);
+ (void)dns_peer_getrequestexpire(peer, &reqexpire);
+ result = dns_peer_getforcetcp(peer, &usetcp);
+ if (result == ISC_R_SUCCESS && usetcp)
+ options |= DNS_REQUESTOPT_TCP;
+ }
+ }
+
+ 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;
+ if (!have_xfrdscp)
+ dscp = zone->altxfrsource4dscp;
+ } else if (!have_xfrsource) {
+ zone->sourceaddr = zone->xfrsource4;
+ if (!have_xfrdscp)
+ dscp = zone->xfrsource4dscp;
+ }
+ 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;
+ if (!have_xfrdscp)
+ dscp = zone->altxfrsource6dscp;
+ } else if (!have_xfrsource) {
+ zone->sourceaddr = zone->xfrsource6;
+ if (!have_xfrdscp)
+ dscp = zone->xfrsource6dscp;
+ }
+ break;
+ default:
+ result = ISC_R_NOTIMPLEMENTED;
+ goto cleanup;
+ }
+
+ if (!DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NOEDNS)) {
+ result = add_opt(message, udpsize, reqnsid, reqexpire);
+ 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_createvia4(zone->view->requestmgr, message,
+ &zone->sourceaddr, &zone->masteraddr,
+ dscp, options, key, timeout * 3,
+ timeout, 0, zone->task,
+ refresh_callback, zone, &zone->request);
+ if (result != ISC_R_SUCCESS) {
+ zone_idetach(&dummy);
+ zone_debuglog(zone, me, 1,
+ "dns_request_createvia4() failed: %s",
+ dns_result_totext(result));
+ goto skip_master;
+ } else {
+ if (isc_sockaddr_pf(&zone->masteraddr) == PF_INET)
+ inc_stats(zone, dns_zonestatscounter_soaoutv4);
+ else
+ inc_stats(zone, dns_zonestatscounter_soaoutv6);
+ }
+ cancel = 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);
+ dns_message_destroy(&message);
+ /*
+ * 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;
+ bool have_xfrsource = false, have_xfrdscp = false;
+ bool reqnsid;
+ uint16_t udpsize = SEND_BUFFER_SIZE;
+ isc_dscp_t dscp = -1;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(LOCKED_ZONE(zone));
+ REQUIRE((soardataset != NULL && stub == NULL) ||
+ (soardataset == NULL && stub != NULL));
+ REQUIRE(stub == NULL || DNS_STUB_VALID(stub));
+
+ ENTER;
+
+ 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);
+ }
+
+ result = dns_db_newversion(stub->db, &stub->version);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_INFO, "refreshing stub: "
+ "dns_db_newversion() failed: %s",
+ dns_result_totext(result));
+ goto cleanup;
+ }
+
+ /*
+ * Update SOA record.
+ */
+ result = dns_db_findnode(stub->db, &zone->origin, 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(result == ISC_R_SUCCESS);
+
+ 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;
+ bool 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 = true;
+ result = dns_peer_gettransferdscp(peer, &dscp);
+ if (result == ISC_R_SUCCESS && dscp != -1)
+ have_xfrdscp = 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, false);
+ 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;
+ if (!have_xfrdscp)
+ dscp = zone->altxfrsource4dscp;
+ } else if (!have_xfrsource) {
+ zone->sourceaddr = zone->xfrsource4;
+ if (!have_xfrdscp)
+ dscp = zone->xfrsource4dscp;
+ }
+ break;
+ case PF_INET6:
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_USEALTXFRSRC)) {
+ zone->sourceaddr = zone->altxfrsource6;
+ if (!have_xfrdscp)
+ dscp = zone->altxfrsource6dscp;
+ } else if (!have_xfrsource) {
+ zone->sourceaddr = zone->xfrsource6;
+ if (!have_xfrdscp)
+ dscp = zone->xfrsource6dscp;
+ }
+ break;
+ default:
+ result = ISC_R_NOTIMPLEMENTED;
+ POST(result);
+ goto cleanup;
+ }
+ timeout = 15;
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DIALREFRESH))
+ timeout = 30;
+ result = dns_request_createvia4(zone->view->requestmgr, message,
+ &zone->sourceaddr, &zone->masteraddr,
+ dscp, DNS_REQUESTOPT_TCP, key,
+ timeout * 3, timeout, 0, 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,
+ 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);
+ 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;
+ bool free_needed, linked = false;
+ dns_zone_t *raw = NULL, *secure = NULL;
+
+ 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 = true;
+ zone->statelist = NULL;
+ }
+ if (zone->statelist == &zone->zmgr->xfrin_in_progress) {
+ ISC_LIST_UNLINK(zone->zmgr->xfrin_in_progress, zone,
+ statelink);
+ zone->statelist = NULL;
+ zmgr_resume_xfrs(zone->zmgr, false);
+ }
+ 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);
+
+ /* Safe to release the zone now */
+ if (zone->zmgr != NULL)
+ dns_zonemgr_releasezone(zone->zmgr, zone);
+
+ LOCK_ZONE(zone);
+ INSIST(zone != zone->raw);
+ 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);
+
+ forward_cancel(zone);
+
+ if (zone->timer != NULL) {
+ isc_timer_detach(&zone->timer);
+ INSIST(zone->irefs > 0);
+ zone->irefs--;
+ }
+
+ /*
+ * 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);
+ if (inline_secure(zone)) {
+ raw = zone->raw;
+ zone->raw = NULL;
+ }
+ if (inline_raw(zone)) {
+ secure = zone->secure;
+ zone->secure = NULL;
+ }
+ UNLOCK_ZONE(zone);
+ if (raw != NULL)
+ dns_zone_detach(&raw);
+ if (secure != NULL)
+ dns_zone_idetach(&secure);
+ 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));
+ ENTER;
+
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_EXITING))
+ return;
+
+ isc_time_settoepoch(&next);
+
+ switch (zone->type) {
+ case dns_zone_redirect:
+ if (zone->masters != NULL)
+ goto treat_as_slave;
+ /* FALLTHROUGH */
+
+ case dns_zone_master:
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDNOTIFY) ||
+ DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDSTARTUPNOTIFY))
+ 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 (zone->type == dns_zone_redirect)
+ break;
+ if (!DNS_ZONE_FLAG(zone, DNS_ZONEFLG_REFRESHING) &&
+ !isc_time_isepoch(&zone->refreshkeytime)) {
+ if (isc_time_isepoch(&next) ||
+ isc_time_compare(&zone->refreshkeytime, &next) < 0)
+ next = zone->refreshkeytime;
+ }
+ 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:
+ treat_as_slave:
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDNOTIFY) ||
+ DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDSTARTUPNOTIFY))
+ 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) &&
+ !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADPENDING) &&
+ !isc_time_isepoch(&zone->refreshtime) &&
+ (isc_time_isepoch(&next) ||
+ isc_time_compare(&zone->refreshtime, &next) < 0))
+ next = zone->refreshtime;
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADED) &&
+ !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;
+
+ case dns_zone_key:
+ 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 (!DNS_ZONE_FLAG(zone, DNS_ZONEFLG_REFRESHING)) {
+ if (isc_time_isepoch(&next) ||
+ (!isc_time_isepoch(&zone->refreshkeytime) &&
+ isc_time_compare(&zone->refreshkeytime, &next) < 0))
+ next = zone->refreshkeytime;
+ }
+ 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, 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, 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_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, 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->ttl = rdataset.ttl;
+ ISC_LIST_APPEND(temprdatalist->rdata, temprdata, link);
+
+ 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, 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)
+{
+ return (dns_zone_notifyreceive2(zone, from, NULL, msg));
+}
+
+isc_result_t
+dns_zone_notifyreceive2(dns_zone_t *zone, isc_sockaddr_t *from,
+ isc_sockaddr_t *to, 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;
+ uint32_t serial = 0;
+ bool have_serial = false;
+ dns_tsigkey_t *tsigkey;
+ dns_name_t *tsig;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ /*
+ * If type != T_SOA return DNS_R_NOTIMP. 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));
+
+ /*
+ * Notify messages are processed by the raw zone.
+ */
+ LOCK_ZONE(zone);
+ INSIST(zone != zone->raw);
+ if (inline_secure(zone)) {
+ result = dns_zone_notifyreceive2(zone->raw, from, to, msg);
+ UNLOCK_ZONE(zone);
+ return (result);
+ }
+ /*
+ * We only handle NOTIFY (SOA) at the present.
+ */
+ 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'.
+ */
+ tsigkey = dns_message_gettsigkey(msg);
+ tsig = dns_tsigkey_identity(tsigkey);
+ if (i >= zone->masterscnt && zone->notify_acl != NULL &&
+ dns_acl_match(&netaddr, tsig, 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) {
+ uint32_t oldserial;
+ unsigned int soacount;
+
+ dns_rdataset_current(rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &soa, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ serial = soa.serial;
+ have_serial = true;
+ /*
+ * The following should safely be performed without DB
+ * lock and succeed in this context.
+ */
+ result = zone_get_from_db(zone, zone->db, NULL,
+ &soacount, &oldserial, NULL,
+ NULL, NULL, NULL, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ RUNTIME_CHECK(soacount > 0U);
+ if (isc_serial_le(serial, oldserial)) {
+ 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);
+ if (have_serial)
+ dns_zone_log(zone, ISC_LOG_INFO,
+ "notify from %s: serial %u: refresh in "
+ "progress, refresh check queued",
+ fromtext, serial);
+ else
+ dns_zone_log(zone, ISC_LOG_INFO,
+ "notify from %s: refresh in progress, "
+ "refresh check queued", fromtext);
+ return (ISC_R_SUCCESS);
+ }
+ if (have_serial)
+ dns_zone_log(zone, ISC_LOG_INFO, "notify from %s: serial %u",
+ fromtext, serial);
+ else
+ dns_zone_log(zone, ISC_LOG_INFO, "notify from %s: no serial",
+ fromtext);
+ zone->notifyfrom = *from;
+ UNLOCK_ZONE(zone);
+
+ if (to != NULL) {
+ dns_zonemgr_unreachabledel(zone->zmgr, from, to);
+ }
+ 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);
+}
+
+bool
+dns_zone_getupdatedisabled(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ return (zone->update_disabled);
+
+}
+
+void
+dns_zone_setupdatedisabled(dns_zone_t *zone, bool state) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ zone->update_disabled = state;
+}
+
+bool
+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, bool 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, int32_t size) {
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ zone->journalsize = size;
+}
+
+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, (unsigned int)length - 1);
+ if (zone->type != dns_zone_redirect && zone->type != dns_zone_key) {
+ if (dns_name_dynamic(&zone->origin))
+ result = dns_name_totext(&zone->origin, 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);
+ }
+ if (inline_secure(zone) && 9U < isc_buffer_availablelength(&buffer))
+ isc_buffer_putstr(&buffer, " (signed)");
+ if (inline_raw(zone) && 11U < isc_buffer_availablelength(&buffer))
+ isc_buffer_putstr(&buffer, " (unsigned)");
+
+ 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, (unsigned int)length - 1);
+ if (dns_name_dynamic(&zone->origin))
+ result = dns_name_totext(&zone->origin, 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, (unsigned int)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, (unsigned int)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);
+}
+
+void
+dns_zone_nameonly(dns_zone_t *zone, char *buf, size_t length) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(buf != NULL);
+ zone_name_tostr(zone, buf, length);
+}
+
+void
+dns_zone_logv(dns_zone_t *zone, isc_logcategory_t *category, int level,
+ const char *prefix, const char *fmt, va_list ap)
+{
+ char message[4096];
+ const char *zstr;
+
+ if (!isc_log_wouldlog(dns_lctx, level)) {
+ return;
+ }
+
+ vsnprintf(message, sizeof(message), fmt, ap);
+
+ switch (zone->type) {
+ case dns_zone_key:
+ zstr = "managed-keys-zone";
+ break;
+ case dns_zone_redirect:
+ zstr = "redirect-zone";
+ break;
+ default:
+ zstr = "zone ";
+ }
+
+ isc_log_write(dns_lctx, category, DNS_LOGMODULE_ZONE, level,
+ "%s%s%s%s: %s",
+ (prefix != NULL ? prefix : ""),
+ (prefix != NULL ? ": " : ""),
+ zstr, zone->strnamerd, message);
+}
+
+static void
+notify_log(dns_zone_t *zone, int level, const char *fmt, ...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ dns_zone_logv(zone, DNS_LOGCATEGORY_NOTIFY, level, NULL, fmt, ap);
+ va_end(ap);
+}
+
+void
+dns_zone_logc(dns_zone_t *zone, isc_logcategory_t *category,
+ int level, const char *fmt, ...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ dns_zone_logv(zone, category, level, NULL, fmt, ap);
+ va_end(ap);
+}
+
+void
+dns_zone_log(dns_zone_t *zone, int level, const char *fmt, ...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ dns_zone_logv(zone, DNS_LOGCATEGORY_GENERAL, level, NULL, fmt, ap);
+ va_end(ap);
+}
+
+static void
+zone_debuglog(dns_zone_t *zone, const char *me, int debuglevel,
+ const char *fmt, ...)
+{
+ int level = ISC_LOG_DEBUG(debuglevel);
+ va_list ap;
+
+ va_start(ap, fmt);
+ dns_zone_logv(zone, DNS_LOGCATEGORY_GENERAL, level, me, fmt, ap);
+ va_end(ap);
+}
+
+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, uint32_t maxxfrin) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ zone->maxxfrin = maxxfrin;
+}
+
+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, uint32_t maxxfrout) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ zone->maxxfrout = maxxfrout;
+}
+
+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, uint32_t idlein) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ if (idlein == 0)
+ idlein = DNS_DEFAULT_IDLEIN;
+ zone->idlein = idlein;
+}
+
+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, uint32_t idleout) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ zone->idleout = idleout;
+}
+
+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) {
+ bool startup;
+
+ notify->flags |= DNS_NOTIFY_NOSOA;
+ dns_request_destroy(&notify->request);
+ startup = (notify->flags & DNS_NOTIFY_STARTUP);
+ result = notify_send_queue(notify, startup);
+ if (result != ISC_R_SUCCESS)
+ notify_destroy(notify, false);
+ } else {
+ if (result == ISC_R_TIMEDOUT)
+ notify_log(notify->zone, ISC_LOG_DEBUG(1),
+ "notify to %s: retries exceeded", addrbuf);
+ notify_destroy(notify, false);
+ }
+ if (message != NULL)
+ dns_message_destroy(&message);
+}
+
+struct secure_event {
+ isc_event_t e;
+ dns_db_t *db;
+ uint32_t serial;
+};
+
+static void
+update_log_cb(void *arg, dns_zone_t *zone, int level, const char *message) {
+ UNUSED(arg);
+ dns_zone_log(zone, level, "%s", message);
+}
+
+static isc_result_t
+sync_secure_journal(dns_zone_t *zone, dns_zone_t *raw, dns_journal_t *journal,
+ uint32_t start, uint32_t end,
+ dns_difftuple_t **soatuplep, dns_diff_t *diff)
+{
+ isc_result_t result;
+ dns_difftuple_t *tuple = NULL;
+ dns_diffop_t op = DNS_DIFFOP_ADD;
+ int n_soa = 0;
+
+ REQUIRE(soatuplep != NULL);
+
+ if (start == end)
+ return (DNS_R_UNCHANGED);
+
+ CHECK(dns_journal_iter_init(journal, start, end));
+ for (result = dns_journal_first_rr(journal);
+ result == ISC_R_SUCCESS;
+ result = dns_journal_next_rr(journal))
+ {
+ dns_name_t *name = NULL;
+ uint32_t ttl;
+ dns_rdata_t *rdata = NULL;
+ dns_journal_current_rr(journal, &name, &ttl, &rdata);
+
+ if (rdata->type == dns_rdatatype_soa) {
+ n_soa++;
+ if (n_soa == 2) {
+ /*
+ * Save the latest raw SOA record.
+ */
+ if (*soatuplep != NULL)
+ dns_difftuple_free(soatuplep);
+ CHECK(dns_difftuple_create(diff->mctx,
+ DNS_DIFFOP_ADD,
+ name, ttl, rdata,
+ soatuplep));
+ }
+ if (n_soa == 3)
+ n_soa = 1;
+ continue;
+ }
+
+ /* Sanity. */
+ if (n_soa == 0) {
+ dns_zone_log(raw, ISC_LOG_ERROR,
+ "corrupt journal file: '%s'\n",
+ raw->journal);
+ return (ISC_R_FAILURE);
+ }
+
+ if (zone->privatetype != 0 &&
+ rdata->type == zone->privatetype)
+ continue;
+
+ if (rdata->type == dns_rdatatype_nsec ||
+ rdata->type == dns_rdatatype_rrsig ||
+ rdata->type == dns_rdatatype_nsec3 ||
+ rdata->type == dns_rdatatype_dnskey ||
+ rdata->type == dns_rdatatype_nsec3param)
+ continue;
+
+ op = (n_soa == 1) ? DNS_DIFFOP_DEL : DNS_DIFFOP_ADD;
+
+ CHECK(dns_difftuple_create(diff->mctx, op, name, ttl, rdata,
+ &tuple));
+ dns_diff_appendminimal(diff, &tuple);
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+
+ failure:
+ return(result);
+}
+
+static isc_result_t
+sync_secure_db(dns_zone_t *seczone, dns_zone_t *raw, dns_db_t *secdb,
+ dns_dbversion_t *secver, dns_difftuple_t **soatuple,
+ dns_diff_t *diff)
+{
+ isc_result_t result;
+ dns_db_t *rawdb = NULL;
+ dns_dbversion_t *rawver = NULL;
+ dns_difftuple_t *tuple = NULL, *next;
+ dns_difftuple_t *oldtuple = NULL, *newtuple = NULL;
+ dns_rdata_soa_t oldsoa, newsoa;
+
+ REQUIRE(DNS_ZONE_VALID(seczone));
+ REQUIRE(soatuple != NULL && *soatuple == NULL);
+
+ if (!seczone->sourceserialset)
+ return (DNS_R_UNCHANGED);
+
+ dns_db_attach(raw->db, &rawdb);
+ dns_db_currentversion(rawdb, &rawver);
+ result = dns_db_diffx(diff, rawdb, rawver, secdb, secver, NULL);
+ dns_db_closeversion(rawdb, &rawver, false);
+ dns_db_detach(&rawdb);
+
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ for (tuple = ISC_LIST_HEAD(diff->tuples);
+ tuple != NULL;
+ tuple = next)
+ {
+ next = ISC_LIST_NEXT(tuple, link);
+ if (tuple->rdata.type == dns_rdatatype_nsec ||
+ tuple->rdata.type == dns_rdatatype_rrsig ||
+ tuple->rdata.type == dns_rdatatype_dnskey ||
+ tuple->rdata.type == dns_rdatatype_nsec3 ||
+ tuple->rdata.type == dns_rdatatype_nsec3param)
+ {
+ ISC_LIST_UNLINK(diff->tuples, tuple, link);
+ dns_difftuple_free(&tuple);
+ continue;
+ }
+ if (tuple->rdata.type == dns_rdatatype_soa) {
+ if (tuple->op == DNS_DIFFOP_DEL) {
+ INSIST(oldtuple == NULL);
+ oldtuple = tuple;
+ }
+ if (tuple->op == DNS_DIFFOP_ADD) {
+ INSIST(newtuple == NULL);
+ newtuple = tuple;
+ }
+ }
+ }
+
+ if (oldtuple != NULL && newtuple != NULL) {
+
+ result = dns_rdata_tostruct(&oldtuple->rdata, &oldsoa, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ result = dns_rdata_tostruct(&newtuple->rdata, &newsoa, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ /*
+ * If the SOA records are the same except for the serial
+ * remove them from the diff.
+ */
+ if (oldsoa.refresh == newsoa.refresh &&
+ oldsoa.retry == newsoa.retry &&
+ oldsoa.minimum == newsoa.minimum &&
+ oldsoa.expire == newsoa.expire &&
+ dns_name_equal(&oldsoa.origin, &newsoa.origin) &&
+ dns_name_equal(&oldsoa.contact, &newsoa.contact)) {
+ ISC_LIST_UNLINK(diff->tuples, oldtuple, link);
+ dns_difftuple_free(&oldtuple);
+ ISC_LIST_UNLINK(diff->tuples, newtuple, link);
+ dns_difftuple_free(&newtuple);
+ }
+ }
+
+ if (ISC_LIST_EMPTY(diff->tuples))
+ return (DNS_R_UNCHANGED);
+
+ /*
+ * If there are still SOA records in the diff they can now be removed
+ * saving the new SOA record.
+ */
+ if (oldtuple != NULL) {
+ ISC_LIST_UNLINK(diff->tuples, oldtuple, link);
+ dns_difftuple_free(&oldtuple);
+ }
+
+ if (newtuple != NULL) {
+ ISC_LIST_UNLINK(diff->tuples, newtuple, link);
+ *soatuple = newtuple;
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+receive_secure_serial(isc_task_t *task, isc_event_t *event) {
+ static char me[] = "receive_secure_serial";
+ isc_result_t result = ISC_R_SUCCESS;
+ dns_journal_t *rjournal = NULL;
+ dns_journal_t *sjournal = NULL;
+ uint32_t start, end;
+ dns_zone_t *zone;
+ dns_difftuple_t *tuple = NULL, *soatuple = NULL;
+ dns_update_log_t log = { update_log_cb, NULL };
+ uint32_t newserial = 0, desired = 0;
+ isc_time_t timenow;
+
+ UNUSED(task);
+
+ zone = event->ev_arg;
+ end = ((struct secure_event *)event)->serial;
+
+ ENTER;
+
+ LOCK_ZONE(zone);
+
+ /*
+ * If we are already processing a receive secure serial event
+ * for the zone, just queue the new one and exit.
+ */
+ if (zone->rss_event != NULL && zone->rss_event != event) {
+ ISC_LIST_APPEND(zone->rss_events, event, ev_link);
+ UNLOCK_ZONE(zone);
+ return;
+ }
+
+ nextevent:
+ if (zone->rss_event != NULL) {
+ INSIST(zone->rss_event == event);
+ UNLOCK_ZONE(zone);
+ } else {
+ zone->rss_event = event;
+ dns_diff_init(zone->mctx, &zone->rss_diff);
+
+ /*
+ * zone->db may be NULL, if the load from disk failed.
+ */
+ result = ISC_R_SUCCESS;
+ ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read);
+ if (zone->db != NULL)
+ dns_db_attach(zone->db, &zone->rss_db);
+ else
+ result = ISC_R_FAILURE;
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
+
+ if (result == ISC_R_SUCCESS && zone->raw != NULL)
+ dns_zone_attach(zone->raw, &zone->rss_raw);
+ else
+ result = ISC_R_FAILURE;
+
+ UNLOCK_ZONE(zone);
+
+ CHECK(result);
+
+ /*
+ * We first attempt to sync the raw zone to the secure zone
+ * by using the raw zone's journal, applying all the deltas
+ * from the latest source-serial of the secure zone up to
+ * the current serial number of the raw zone.
+ *
+ * If that fails, then we'll fall back to a direct comparison
+ * between raw and secure zones.
+ */
+ CHECK(dns_journal_open(zone->rss_raw->mctx,
+ zone->rss_raw->journal,
+ DNS_JOURNAL_WRITE, &rjournal));
+
+ result = dns_journal_open(zone->mctx, zone->journal,
+ DNS_JOURNAL_READ, &sjournal);
+ if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND)
+ goto failure;
+
+ if (!dns_journal_get_sourceserial(rjournal, &start)) {
+ start = dns_journal_first_serial(rjournal);
+ dns_journal_set_sourceserial(rjournal, start);
+ }
+ if (sjournal != NULL) {
+ uint32_t serial;
+ /*
+ * We read the secure journal first, if that
+ * exists use its value provided it is greater
+ * that from the raw journal.
+ */
+ if (dns_journal_get_sourceserial(sjournal, &serial)) {
+ if (isc_serial_gt(serial, start))
+ start = serial;
+ }
+ dns_journal_destroy(&sjournal);
+ }
+
+ dns_db_currentversion(zone->rss_db, &zone->rss_oldver);
+ CHECK(dns_db_newversion(zone->rss_db, &zone->rss_newver));
+
+ /*
+ * Try to apply diffs from the raw zone's journal to the secure
+ * zone. If that fails, we recover by syncing up the databases
+ * directly.
+ */
+ result = sync_secure_journal(zone, zone->rss_raw, rjournal,
+ start, end, &soatuple,
+ &zone->rss_diff);
+ if (result == DNS_R_UNCHANGED)
+ goto failure;
+ else if (result != ISC_R_SUCCESS)
+ CHECK(sync_secure_db(zone, zone->rss_raw, zone->rss_db,
+ zone->rss_oldver, &soatuple,
+ &zone->rss_diff));
+
+ CHECK(dns_diff_apply(&zone->rss_diff, zone->rss_db,
+ zone->rss_newver));
+
+ if (soatuple != NULL) {
+ uint32_t oldserial;
+
+ CHECK(dns_db_createsoatuple(zone->rss_db,
+ zone->rss_oldver,
+ zone->rss_diff.mctx,
+ DNS_DIFFOP_DEL, &tuple));
+ oldserial = dns_soa_getserial(&tuple->rdata);
+ newserial = desired =
+ dns_soa_getserial(&soatuple->rdata);
+ if (!isc_serial_gt(newserial, oldserial)) {
+ newserial = oldserial + 1;
+ if (newserial == 0)
+ newserial++;
+ dns_soa_setserial(newserial, &soatuple->rdata);
+ }
+ CHECK(do_one_tuple(&tuple, zone->rss_db,
+ zone->rss_newver, &zone->rss_diff));
+ CHECK(do_one_tuple(&soatuple, zone->rss_db,
+ zone->rss_newver, &zone->rss_diff));
+ } else
+ CHECK(update_soa_serial(zone->rss_db, zone->rss_newver,
+ &zone->rss_diff, zone->mctx,
+ zone->updatemethod));
+
+ }
+ result = dns_update_signaturesinc(&log, zone, zone->rss_db,
+ zone->rss_oldver, zone->rss_newver,
+ &zone->rss_diff,
+ zone->sigvalidityinterval,
+ &zone->rss_state);
+ if (result == DNS_R_CONTINUE) {
+ if (rjournal != NULL)
+ dns_journal_destroy(&rjournal);
+ isc_task_send(task, &event);
+ fprintf(stderr, "looping on dns_update_signaturesinc\n");
+ return;
+ }
+ /*
+ * If something went wrong while trying to update the secure zone and
+ * the latter was already signed before, do not apply raw zone deltas
+ * to it as that would break existing DNSSEC signatures. However, if
+ * the secure zone was not yet signed (e.g. because no signing keys
+ * were created for it), commence applying raw zone deltas to it so
+ * that contents of the raw zone and the secure zone are kept in sync.
+ */
+ if (result != ISC_R_SUCCESS && dns_db_issecure(zone->rss_db)) {
+ goto failure;
+ }
+
+ if (rjournal == NULL)
+ CHECK(dns_journal_open(zone->rss_raw->mctx,
+ zone->rss_raw->journal,
+ DNS_JOURNAL_WRITE, &rjournal));
+ CHECK(zone_journal(zone, &zone->rss_diff, &end,
+ "receive_secure_serial"));
+
+ dns_journal_set_sourceserial(rjournal, end);
+ dns_journal_commit(rjournal);
+
+ LOCK_ZONE(zone);
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NEEDNOTIFY);
+
+ zone->sourceserial = end;
+ zone->sourceserialset = true;
+ zone_needdump(zone, DNS_DUMP_DELAY);
+
+ TIME_NOW(&timenow);
+ zone_settimer(zone, &timenow);
+ UNLOCK_ZONE(zone);
+
+ dns_db_closeversion(zone->rss_db, &zone->rss_oldver, false);
+ dns_db_closeversion(zone->rss_db, &zone->rss_newver, true);
+
+ if (newserial != 0) {
+ dns_zone_log(zone, ISC_LOG_INFO, "serial %u (unsigned %u)",
+ newserial, desired);
+ }
+
+ failure:
+ isc_event_free(&zone->rss_event);
+ event = ISC_LIST_HEAD(zone->rss_events);
+
+ if (zone->rss_raw != NULL)
+ dns_zone_detach(&zone->rss_raw);
+ if (result != ISC_R_SUCCESS)
+ dns_zone_log(zone, ISC_LOG_ERROR, "receive_secure_serial: %s",
+ dns_result_totext(result));
+ if (tuple != NULL)
+ dns_difftuple_free(&tuple);
+ if (soatuple != NULL)
+ dns_difftuple_free(&soatuple);
+ if (zone->rss_db != NULL) {
+ if (zone->rss_oldver != NULL)
+ dns_db_closeversion(zone->rss_db, &zone->rss_oldver,
+ false);
+ if (zone->rss_newver != NULL)
+ dns_db_closeversion(zone->rss_db, &zone->rss_newver,
+ false);
+ dns_db_detach(&zone->rss_db);
+ }
+ INSIST(zone->rss_oldver == NULL);
+ INSIST(zone->rss_newver == NULL);
+ if (rjournal != NULL)
+ dns_journal_destroy(&rjournal);
+ dns_diff_clear(&zone->rss_diff);
+
+ if (event != NULL) {
+ LOCK_ZONE(zone);
+ INSIST(zone->irefs > 1);
+ zone->irefs--;
+ ISC_LIST_UNLINK(zone->rss_events, event, ev_link);
+ goto nextevent;
+ }
+ dns_zone_idetach(&zone);
+}
+
+static isc_result_t
+zone_send_secureserial(dns_zone_t *zone, uint32_t serial) {
+ isc_event_t *e;
+ dns_zone_t *dummy = NULL;
+
+ e = isc_event_allocate(zone->secure->mctx, zone,
+ DNS_EVENT_ZONESECURESERIAL,
+ receive_secure_serial, zone->secure,
+ sizeof(struct secure_event));
+ if (e == NULL)
+ return (ISC_R_NOMEMORY);
+ ((struct secure_event *)e)->serial = serial;
+ INSIST(LOCKED_ZONE(zone->secure));
+ zone_iattach(zone->secure, &dummy);
+ isc_task_send(zone->secure->task, &e);
+
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_SENDSECURE);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+checkandaddsoa(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ dns_rdataset_t *rdataset, uint32_t oldserial)
+{
+ dns_rdata_soa_t soa;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdatalist_t temprdatalist;
+ dns_rdataset_t temprdataset;
+ isc_buffer_t b;
+ isc_result_t result;
+ unsigned char buf[DNS_SOA_BUFFERSIZE];
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+
+ result = dns_rdataset_first(rdataset);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ dns_rdataset_current(rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &soa, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ if (isc_serial_gt(soa.serial, oldserial))
+ return (dns_db_addrdataset(db, node, version, 0, rdataset, 0,
+ NULL));
+ /*
+ * Always bump the serial.
+ */
+ oldserial++;
+ if (oldserial == 0)
+ oldserial++;
+ soa.serial = oldserial;
+
+ /*
+ * Construct a replacement rdataset.
+ */
+ dns_rdata_reset(&rdata);
+ isc_buffer_init(&b, buf, sizeof(buf));
+ result = dns_rdata_fromstruct(&rdata, rdataset->rdclass,
+ dns_rdatatype_soa, &soa, &b);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ dns_rdatalist_init(&temprdatalist);
+ temprdatalist.rdclass = rdata.rdclass;
+ temprdatalist.type = rdata.type;
+ temprdatalist.ttl = rdataset->ttl;
+ ISC_LIST_APPEND(temprdatalist.rdata, &rdata, link);
+
+ dns_rdataset_init(&temprdataset);
+ result = dns_rdatalist_tordataset(&temprdatalist, &temprdataset);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ name = dns_fixedname_initname(&fixed);
+ result = dns_db_nodefullname(db, node, name);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ dns_rdataset_getownercase(rdataset, name);
+ dns_rdataset_setownercase(&temprdataset, name);
+ return (dns_db_addrdataset(db, node, version, 0, &temprdataset,
+ 0, NULL));
+}
+
+/*
+ * This function should populate an nsec3paramlist_t with the
+ * nsecparam_t data from a zone.
+ */
+static isc_result_t
+save_nsec3param(dns_zone_t *zone, nsec3paramlist_t *nsec3list) {
+ isc_result_t result;
+ dns_dbnode_t *node = NULL;
+ dns_rdataset_t rdataset, prdataset;
+ dns_dbversion_t *version = NULL;
+ nsec3param_t *nsec3param = NULL;
+ nsec3param_t *nsec3p = NULL;
+ nsec3param_t *next;
+ dns_db_t *db = NULL;
+ unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE];
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(nsec3list != NULL);
+ REQUIRE(ISC_LIST_EMPTY(*nsec3list));
+
+ dns_rdataset_init(&rdataset);
+ dns_rdataset_init(&prdataset);
+
+ dns_db_attach(zone->db, &db);
+ CHECK(dns_db_getoriginnode(db, &node));
+
+ 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_SUCCESS)
+ goto getprivate;
+
+ /*
+ * walk nsec3param rdataset making a list of parameters (note that
+ * multiple simultaneous nsec3 chains are annoyingly legal -- this
+ * is why we use an nsec3list, even tho we will usually only have
+ * one)
+ */
+ for (result = dns_rdataset_first(&rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rdataset))
+ {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_t private = DNS_RDATA_INIT;
+
+ dns_rdataset_current(&rdataset, &rdata);
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_ZONE, ISC_LOG_DEBUG(3),
+ "looping through nsec3param data");
+ nsec3param = isc_mem_get(zone->mctx, sizeof(nsec3param_t));
+ if (nsec3param == NULL)
+ CHECK(ISC_R_NOMEMORY);
+ ISC_LINK_INIT(nsec3param, link);
+
+ /*
+ * now transfer the data from the rdata to
+ * the nsec3param
+ */
+ dns_nsec3param_toprivate(&rdata, &private,
+ zone->privatetype, nsec3param->data,
+ sizeof(nsec3param->data));
+ nsec3param->length = private.length;
+ ISC_LIST_APPEND(*nsec3list, nsec3param, link);
+ }
+
+ getprivate:
+ result = dns_db_findrdataset(db, node, version, zone->privatetype,
+ dns_rdatatype_none, 0, &prdataset, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto done;
+
+ /*
+ * walk private type records, converting them to nsec3 parameters
+ * using dns_nsec3param_fromprivate(), do the right thing based on
+ * CREATE and REMOVE flags
+ */
+ for (result = dns_rdataset_first(&prdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&prdataset))
+ {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_t private = DNS_RDATA_INIT;
+
+ dns_rdataset_current(&prdataset, &private);
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_ZONE, ISC_LOG_DEBUG(3),
+ "looping through nsec3param private data");
+
+ /*
+ * Do we have a valid private record?
+ */
+ if (!dns_nsec3param_fromprivate(&private, &rdata,
+ buf, sizeof(buf)))
+ continue;
+
+ /*
+ * Remove any NSEC3PARAM records scheduled to be removed.
+ */
+ if (NSEC3REMOVE(rdata.data[1])) {
+ /*
+ * Zero out the flags.
+ */
+ rdata.data[1] = 0;
+
+ for (nsec3p = ISC_LIST_HEAD(*nsec3list);
+ nsec3p != NULL;
+ nsec3p = next)
+ {
+ next = ISC_LIST_NEXT(nsec3p, link);
+
+ if (nsec3p->length == rdata.length + 1 &&
+ memcmp(rdata.data, nsec3p->data + 1,
+ nsec3p->length - 1) == 0) {
+ ISC_LIST_UNLINK(*nsec3list,
+ nsec3p, link);
+ isc_mem_put(zone->mctx, nsec3p,
+ sizeof(nsec3param_t));
+ }
+ }
+ continue;
+ }
+
+ nsec3param = isc_mem_get(zone->mctx, sizeof(nsec3param_t));
+ if (nsec3param == NULL)
+ CHECK(ISC_R_NOMEMORY);
+ ISC_LINK_INIT(nsec3param, link);
+
+ /*
+ * Copy the remaining private records so the nsec/nsec3
+ * chain gets created.
+ */
+ INSIST(private.length <= sizeof(nsec3param->data));
+ memmove(nsec3param->data, private.data, private.length);
+ nsec3param->length = private.length;
+ ISC_LIST_APPEND(*nsec3list, nsec3param, link);
+ }
+
+ done:
+ if (result == ISC_R_NOMORE || result == ISC_R_NOTFOUND)
+ result = ISC_R_SUCCESS;
+
+ failure:
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ if (version != NULL)
+ dns_db_closeversion(db, &version, false);
+ if (db != NULL)
+ dns_db_detach(&db);
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ if (dns_rdataset_isassociated(&prdataset))
+ dns_rdataset_disassociate(&prdataset);
+ return (result);
+}
+
+/*
+ * Populate new zone db with private type records found by save_nsec3param().
+ */
+static isc_result_t
+restore_nsec3param(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version,
+ nsec3paramlist_t *nsec3list)
+{
+ isc_result_t result;
+ dns_diff_t diff;
+ dns_rdata_t rdata;
+ nsec3param_t *nsec3p = NULL;
+ nsec3param_t *next;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(!ISC_LIST_EMPTY(*nsec3list));
+
+ dns_diff_init(zone->mctx, &diff);
+
+ /*
+ * Loop through the list of private-type records, set the INITIAL
+ * and CREATE flags, and the add the record to the apex of the tree
+ * in db.
+ */
+ for (nsec3p = ISC_LIST_HEAD(*nsec3list);
+ nsec3p != NULL;
+ nsec3p = next)
+ {
+ next = ISC_LIST_NEXT(nsec3p, link);
+ dns_rdata_init(&rdata);
+ nsec3p->data[2] = DNS_NSEC3FLAG_CREATE | DNS_NSEC3FLAG_INITIAL;
+ rdata.length = nsec3p->length;
+ rdata.data = nsec3p->data;
+ rdata.type = zone->privatetype;
+ rdata.rdclass = zone->rdclass;
+ result = update_one_rr(db, version, &diff, DNS_DIFFOP_ADD,
+ &zone->origin, 0, &rdata);
+ if (result != ISC_R_SUCCESS) {
+ break;
+ }
+ }
+
+ dns_diff_clear(&diff);
+ return (result);
+}
+
+static void
+receive_secure_db(isc_task_t *task, isc_event_t *event) {
+ isc_result_t result;
+ dns_zone_t *zone;
+ dns_db_t *rawdb, *db = NULL;
+ dns_dbnode_t *rawnode = NULL, *node = NULL;
+ dns_fixedname_t fname;
+ dns_name_t *name;
+ dns_dbiterator_t *dbiterator = NULL;
+ dns_rdatasetiter_t *rdsit = NULL;
+ dns_rdataset_t rdataset;
+ dns_dbversion_t *version = NULL;
+ isc_time_t loadtime;
+ unsigned int oldserial = 0;
+ bool have_oldserial = false;
+ nsec3paramlist_t nsec3list;
+ isc_event_t *setnsec3param_event;
+ dns_zone_t *dummy;
+
+ UNUSED(task);
+
+ ISC_LIST_INIT(nsec3list);
+
+ zone = event->ev_arg;
+ rawdb = ((struct secure_event *)event)->db;
+ isc_event_free(&event);
+
+ name = dns_fixedname_initname(&fname);
+ dns_rdataset_init(&rdataset);
+
+ LOCK_ZONE(zone);
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_EXITING) || !inline_secure(zone)) {
+ result = ISC_R_SHUTTINGDOWN;
+ goto failure;
+ }
+
+ TIME_NOW(&loadtime);
+ ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read);
+ if (zone->db != NULL) {
+ result = dns_db_getsoaserial(zone->db, NULL, &oldserial);
+ if (result == ISC_R_SUCCESS)
+ have_oldserial = true;
+
+ /*
+ * assemble nsec3parameters from the old zone, and set a flag
+ * if any are found
+ */
+ result = save_nsec3param(zone, &nsec3list);
+ if (result != ISC_R_SUCCESS) {
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
+ goto failure;
+ }
+ }
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
+
+ result = dns_db_create(zone->mctx, zone->db_argv[0],
+ &zone->origin, dns_dbtype_zone, zone->rdclass,
+ zone->db_argc - 1, zone->db_argv + 1, &db);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ result = dns_db_newversion(db, &version);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ result = dns_db_createiterator(rawdb, 0, &dbiterator);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ for (result = dns_dbiterator_first(dbiterator);
+ result == ISC_R_SUCCESS;
+ result = dns_dbiterator_next(dbiterator)) {
+ result = dns_dbiterator_current(dbiterator, &rawnode, name);
+ if (result != ISC_R_SUCCESS)
+ continue;
+
+ result = dns_db_findnode(db, name, true, &node);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ result = dns_db_allrdatasets(rawdb, rawnode, NULL, 0, &rdsit);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ for (result = dns_rdatasetiter_first(rdsit);
+ result == ISC_R_SUCCESS;
+ result = dns_rdatasetiter_next(rdsit)) {
+ dns_rdatasetiter_current(rdsit, &rdataset);
+ if (rdataset.type == dns_rdatatype_nsec ||
+ rdataset.type == dns_rdatatype_rrsig ||
+ rdataset.type == dns_rdatatype_nsec3 ||
+ rdataset.type == dns_rdatatype_dnskey ||
+ rdataset.type == dns_rdatatype_nsec3param) {
+ dns_rdataset_disassociate(&rdataset);
+ continue;
+ }
+ if (rdataset.type == dns_rdatatype_soa &&
+ have_oldserial) {
+ result = checkandaddsoa(db, node, version,
+ &rdataset, oldserial);
+ } else
+ result = dns_db_addrdataset(db, node, version,
+ 0, &rdataset, 0,
+ NULL);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ dns_rdataset_disassociate(&rdataset);
+ }
+ dns_rdatasetiter_destroy(&rdsit);
+ dns_db_detachnode(rawdb, &rawnode);
+ dns_db_detachnode(db, &node);
+ }
+
+ /*
+ * Call restore_nsec3param() to create private-type records from
+ * the old nsec3 parameters and insert them into db
+ */
+ if (!ISC_LIST_EMPTY(nsec3list)) {
+ result = restore_nsec3param(zone, db, version, &nsec3list);
+ if (result != ISC_R_SUCCESS) {
+ goto failure;
+ }
+ }
+
+ dns_db_closeversion(db, &version, true);
+
+ /*
+ * Lock hierarchy: zmgr, zone, raw.
+ */
+ INSIST(zone != zone->raw);
+ LOCK_ZONE(zone->raw);
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NEEDNOTIFY);
+ result = zone_postload(zone, db, loadtime, ISC_R_SUCCESS);
+ zone_needdump(zone, 0); /* XXXMPA */
+ UNLOCK_ZONE(zone->raw);
+
+ /*
+ * Process any queued NSEC3PARAM change requests.
+ */
+ while (!ISC_LIST_EMPTY(zone->setnsec3param_queue)) {
+ setnsec3param_event = ISC_LIST_HEAD(zone->setnsec3param_queue);
+ ISC_LIST_UNLINK(zone->setnsec3param_queue, setnsec3param_event,
+ ev_link);
+ dummy = NULL;
+ zone_iattach(zone, &dummy);
+ isc_task_send(zone->task, &setnsec3param_event);
+ }
+
+ failure:
+ UNLOCK_ZONE(zone);
+ if (result != ISC_R_SUCCESS)
+ dns_zone_log(zone, ISC_LOG_ERROR, "receive_secure_db: %s",
+ dns_result_totext(result));
+
+ while (!ISC_LIST_EMPTY(nsec3list)) {
+ nsec3param_t *nsec3p;
+ nsec3p = ISC_LIST_HEAD(nsec3list);
+ ISC_LIST_UNLINK(nsec3list, nsec3p, link);
+ isc_mem_put(zone->mctx, nsec3p, sizeof(nsec3param_t));
+ }
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ if (db != NULL) {
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ if (version != NULL)
+ dns_db_closeversion(db, &version, false);
+ dns_db_detach(&db);
+ }
+ if (rawnode != NULL)
+ dns_db_detachnode(rawdb, &rawnode);
+ dns_db_detach(&rawdb);
+ if (dbiterator != NULL)
+ dns_dbiterator_destroy(&dbiterator);
+ dns_zone_idetach(&zone);
+
+ INSIST(version == NULL);
+}
+
+static isc_result_t
+zone_send_securedb(dns_zone_t *zone, dns_db_t *db) {
+ isc_event_t *e;
+ dns_db_t *dummy = NULL;
+ dns_zone_t *secure = NULL;
+
+ e = isc_event_allocate(zone->secure->mctx, zone,
+ DNS_EVENT_ZONESECUREDB,
+ receive_secure_db, zone->secure,
+ sizeof(struct secure_event));
+ if (e == NULL)
+ return (ISC_R_NOMEMORY);
+ dns_db_attach(db, &dummy);
+ ((struct secure_event *)e)->db = dummy;
+ INSIST(LOCKED_ZONE(zone->secure));
+ zone_iattach(zone->secure, &secure);
+ isc_task_send(zone->secure->task, &e);
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_SENDSECURE);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_zone_replacedb(dns_zone_t *zone, dns_db_t *db, bool dump) {
+ isc_result_t result;
+ dns_zone_t *secure = NULL;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+ again:
+ LOCK_ZONE(zone);
+ if (inline_raw(zone)) {
+ secure = zone->secure;
+ INSIST(secure != zone);
+ TRYLOCK_ZONE(result, secure);
+ if (result != ISC_R_SUCCESS) {
+ UNLOCK_ZONE(zone);
+ secure = NULL;
+#if ISC_PLATFORM_USETHREADS
+ isc_thread_yield();
+#endif
+ goto again;
+ }
+ }
+ ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_write);
+ result = zone_replacedb(zone, db, dump);
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_write);
+ if (secure != NULL)
+ UNLOCK_ZONE(secure);
+ UNLOCK_ZONE(zone);
+ return (result);
+}
+
+static isc_result_t
+zone_replacedb(dns_zone_t *zone, dns_db_t *db, bool 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));
+ if (inline_raw(zone))
+ REQUIRE(LOCKED_ZONE(zone->secure));
+
+ result = dns_db_rpz_ready(db);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ 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 && zone->type != dns_zone_key) {
+ 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 journaled 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))
+ {
+ uint32_t serial, oldserial;
+
+ 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.
+ */
+ result = zone_get_from_db(zone, zone->db, NULL, &soacount,
+ &oldserial, NULL, NULL, NULL, NULL,
+ NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ RUNTIME_CHECK(soacount > 0U);
+ if ((zone->type == dns_zone_slave ||
+ (zone->type == dns_zone_redirect &&
+ zone->masters != NULL))
+ && !isc_serial_gt(serial, oldserial)) {
+ uint32_t serialmin, serialmax;
+ serialmin = (oldserial + 1) & 0xffffffffU;
+ serialmax = (oldserial + 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) {
+ char strbuf[ISC_STRERRORSIZE];
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "ixfr-from-differences: failed: "
+ "%s", strbuf);
+ goto fallback;
+ }
+ 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;
+ }
+ }
+ if (zone->type == dns_zone_master && inline_raw(zone))
+ zone_send_secureserial(zone, serial);
+ } else {
+ fallback:
+ if (dump && zone->masterfile != NULL) {
+ /*
+ * If DNS_ZONEFLG_FORCEXFER was set we don't want
+ * to keep the old masterfile.
+ */
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_FORCEXFER) &&
+ remove(zone->masterfile) < 0 && errno != ENOENT) {
+ char strbuf[ISC_STRERRORSIZE];
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ isc_log_write(dns_lctx,
+ DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_ZONE,
+ ISC_LOG_WARNING,
+ "unable to remove masterfile "
+ "'%s': '%s'",
+ zone->masterfile, strbuf);
+ }
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADED) == 0)
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NODELAY);
+ else
+ zone_needdump(zone, 0);
+ }
+ 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
+ * journaled 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");
+ if (remove(zone->journal) < 0 && errno != ENOENT) {
+ char strbuf[ISC_STRERRORSIZE];
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ isc_log_write(dns_lctx,
+ DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_ZONE,
+ ISC_LOG_WARNING,
+ "unable to remove journal "
+ "'%s': '%s'",
+ zone->journal, strbuf);
+ }
+ }
+
+ if (inline_raw(zone))
+ zone_send_securedb(zone, db);
+ }
+
+ dns_db_closeversion(db, &ver, false);
+
+ dns_zone_log(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, 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;
+ bool again = false;
+ unsigned int soacount;
+ unsigned int nscount;
+ uint32_t serial, refresh, retry, expire, minimum;
+ isc_result_t xfrresult = result;
+ bool free_needed;
+ dns_zone_t *secure = NULL;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ dns_zone_log(zone, ISC_LOG_DEBUG(1),
+ "zone transfer finished: %s", dns_result_totext(result));
+
+ /*
+ * Obtaining a lock on the zone->secure (see zone_send_secureserial)
+ * could result in a deadlock due to a LOR so we will spin if we
+ * can't obtain the both locks.
+ */
+ again:
+ LOCK_ZONE(zone);
+ if (inline_raw(zone)) {
+ secure = zone->secure;
+ INSIST(secure != zone);
+ TRYLOCK_ZONE(result, secure);
+ if (result != ISC_R_SUCCESS) {
+ UNLOCK_ZONE(zone);
+ secure = NULL;
+#if ISC_PLATFORM_USETHREADS
+ isc_thread_yield();
+#endif
+ goto again;
+ }
+ }
+
+ 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 (xfrresult) {
+ 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->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",
+ serial, buf);
+ if (inline_raw(zone))
+ zone_send_secureserial(zone, serial);
+ }
+
+ /*
+ * This is not necessary 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) {
+ unsigned int delay = DNS_DUMP_DELAY;
+
+ 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);
+
+ if ((DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NODELAY) != 0) ||
+ result == ISC_R_FILENOTFOUND)
+ delay = 0;
+
+ if ((result == ISC_R_SUCCESS ||
+ result == ISC_R_FILENOTFOUND) &&
+ zone->masterfile != NULL)
+ zone_needdump(zone, 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));
+ }
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NODELAY);
+ 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;
+
+ case DNS_R_TOOMANYRECORDS:
+ DNS_ZONE_JITTER_ADD(&now, zone->refresh, &zone->refreshtime);
+ inc_stats(zone, dns_zonestatscounter_xfrfail);
+ break;
+
+ 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 = true;
+ } else
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_USEALTXFRSRC);
+ } else {
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_REFRESH);
+ again = 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);
+ }
+
+ if (secure != NULL)
+ UNLOCK_ZONE(secure);
+ /*
+ * This transfer finishing freed up a transfer quota slot.
+ * Let any other zones waiting for quota have it.
+ */
+ if (zone->zmgr != NULL &&
+ zone->statelist == &zone->zmgr->xfrin_in_progress) {
+ UNLOCK_ZONE(zone);
+ 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, false);
+ RWUNLOCK(&zone->zmgr->rwlock, isc_rwlocktype_write);
+ LOCK_ZONE(zone);
+ }
+
+ /*
+ * 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;
+ dns_zone_t *secure = NULL;
+
+ REQUIRE(DNS_LOAD_VALID(load));
+ zone = load->zone;
+
+ ENTER;
+
+ tresult = dns_db_endload(load->db, &load->callbacks);
+ if (tresult != ISC_R_SUCCESS &&
+ (result == ISC_R_SUCCESS || result == DNS_R_SEENINCLUDE))
+ result = tresult;
+
+ /*
+ * Lock hierarchy: zmgr, zone, raw.
+ */
+ again:
+ LOCK_ZONE(zone);
+ INSIST(zone != zone->raw);
+ if (inline_secure(zone))
+ LOCK_ZONE(zone->raw);
+ else if (inline_raw(zone)) {
+ secure = zone->secure;
+ TRYLOCK_ZONE(result, secure);
+ if (result != ISC_R_SUCCESS) {
+ UNLOCK_ZONE(zone);
+ secure = NULL;
+#if ISC_PLATFORM_USETHREADS
+ isc_thread_yield();
+#endif
+ goto again;
+ }
+ }
+ (void)zone_postload(zone, load->db, load->loadtime, result);
+ zonemgr_putio(&zone->readio);
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_LOADING);
+ zone_idetach(&load->callbacks.zone);
+ /*
+ * Leave the zone frozen if the reload fails.
+ */
+ if ((result == ISC_R_SUCCESS || result == DNS_R_SEENINCLUDE) &&
+ DNS_ZONE_FLAG(zone, DNS_ZONEFLG_THAW))
+ zone->update_disabled = false;
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_THAW);
+ if (inline_secure(zone))
+ UNLOCK_ZONE(zone->raw);
+ else if (secure != NULL)
+ UNLOCK_ZONE(secure);
+ UNLOCK_ZONE(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, uint32_t interval) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ zone->sigvalidityinterval = interval;
+}
+
+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, uint32_t interval) {
+ isc_time_t now;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ zone->sigresigninginterval = interval;
+ set_resigntime(zone);
+ if (zone->task != NULL) {
+ TIME_NOW(&now);
+ zone_settimer(zone, &now);
+ }
+ UNLOCK_ZONE(zone);
+}
+
+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 = ISC_R_SUCCESS;
+ 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;
+ const char *soa_before = "";
+ isc_dscp_t dscp = -1;
+ bool loaded;
+
+ 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);
+
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_SOABEFOREAXFR))
+ soa_before = "SOA before ";
+ /*
+ * Decide whether we should request IXFR or AXFR.
+ */
+ ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read);
+ loaded = (zone->db != NULL);
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
+
+ if (!loaded) {
+ 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_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 {
+ bool use_ixfr = true;
+ if (peer != NULL)
+ result = dns_peer_getrequestixfr(peer, &use_ixfr);
+ if (peer == NULL || result != ISC_R_SUCCESS)
+ use_ixfr = zone->requestixfr;
+ if (use_ixfr == false) {
+ dns_zone_log(zone, ISC_LOG_DEBUG(1),
+ "IXFR disabled, requesting %sAXFR from %s",
+ soa_before, 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));
+ }
+
+ if (zone->masterdscps != NULL)
+ dscp = zone->masterdscps[zone->curmaster];
+
+ LOCK_ZONE(zone);
+ masteraddr = zone->masteraddr;
+ sourceaddr = zone->sourceaddr;
+ switch (isc_sockaddr_pf(&masteraddr)) {
+ case PF_INET:
+ if (dscp == -1)
+ dscp = zone->xfrsource4dscp;
+ break;
+ case PF_INET6:
+ if (dscp == -1)
+ dscp = zone->xfrsource6dscp;
+ break;
+ default:
+ INSIST(0);
+ };
+ UNLOCK_ZONE(zone);
+ INSIST(isc_sockaddr_pf(&masteraddr) == isc_sockaddr_pf(&sourceaddr));
+ result = dns_xfrin_create3(zone, xfrtype, &masteraddr, &sourceaddr,
+ dscp, 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) {
+ LOCK(&forward->zone->lock);
+ if (ISC_LINK_LINKED(forward, link))
+ ISC_LIST_UNLINK(forward->zone->forwards, forward, link);
+ UNLOCK(&forward->zone->lock);
+ 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;
+ isc_dscp_t dscp = -1;
+
+ LOCK_ZONE(forward->zone);
+
+ if (DNS_ZONE_FLAG(forward->zone, DNS_ZONEFLG_EXITING)) {
+ UNLOCK_ZONE(forward->zone);
+ return (ISC_R_CANCELED);
+ }
+
+ 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;
+ dscp = forward->zone->xfrsource4dscp;
+ break;
+ case PF_INET6:
+ src = forward->zone->xfrsource6;
+ dscp = forward->zone->xfrsource6dscp;
+ break;
+ default:
+ result = ISC_R_NOTIMPLEMENTED;
+ goto unlock;
+ }
+ result = dns_request_createraw4(forward->zone->view->requestmgr,
+ forward->msgbuf,
+ &src, &forward->addr, dscp,
+ forward->options, 15 /* XXX */,
+ 0, 0, forward->zone->task,
+ forward_callback, forward,
+ &forward->request);
+ if (result == ISC_R_SUCCESS) {
+ if (!ISC_LINK_LINKED(forward, link))
+ ISC_LIST_APPEND(forward->zone->forwards, forward, link);
+ }
+
+ 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: {
+ 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_INFO,
+ "forwarded dynamic update: "
+ "master %s returned: %.*s",
+ master, (int)rb.used, rcode);
+ 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;
+ ISC_LINK_INIT(forward, link);
+ forward->magic = FORWARD_MAGIC;
+ forward->options = DNS_REQUESTOPT_TCP;
+ /*
+ * If we have a SIG(0) signed message we need to preserve the
+ * query id as that is included in the SIG(0) computation.
+ */
+ if (msg->sig0 != NULL)
+ forward->options |= DNS_REQUESTOPT_FIXEDID;
+
+ 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;
+
+ 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->loadtasks = NULL;
+ zmgr->mctxpool = NULL;
+ zmgr->task = NULL;
+ zmgr->notifyrl = NULL;
+ zmgr->refreshrl = NULL;
+ zmgr->startupnotifyrl = NULL;
+ zmgr->startuprefreshrl = 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;
+
+ /* Unreachable lock. */
+ result = isc_rwlock_init(&zmgr->urlock, 0, 0);
+ 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_urlock;
+
+ isc_task_setname(zmgr->task, "zmgr", zmgr);
+ result = isc_ratelimiter_create(mctx, timermgr, zmgr->task,
+ &zmgr->notifyrl);
+ if (result != ISC_R_SUCCESS)
+ goto free_task;
+
+ result = isc_ratelimiter_create(mctx, timermgr, zmgr->task,
+ &zmgr->refreshrl);
+ if (result != ISC_R_SUCCESS)
+ goto free_notifyrl;
+
+ result = isc_ratelimiter_create(mctx, timermgr, zmgr->task,
+ &zmgr->startupnotifyrl);
+ if (result != ISC_R_SUCCESS)
+ goto free_refreshrl;
+
+ result = isc_ratelimiter_create(mctx, timermgr, zmgr->task,
+ &zmgr->startuprefreshrl);
+ if (result != ISC_R_SUCCESS)
+ goto free_startupnotifyrl;
+
+ /* default to 20 refresh queries / notifies per second. */
+ setrl(zmgr->notifyrl, &zmgr->notifyrate, 20);
+ setrl(zmgr->startupnotifyrl, &zmgr->startupnotifyrate, 20);
+ setrl(zmgr->refreshrl, &zmgr->serialqueryrate, 20);
+ setrl(zmgr->startuprefreshrl, &zmgr->startupserialqueryrate, 20);
+ isc_ratelimiter_setpushpop(zmgr->startupnotifyrl, true);
+ isc_ratelimiter_setpushpop(zmgr->startuprefreshrl, true);
+
+ 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_startuprefreshrl;
+
+ zmgr->magic = ZONEMGR_MAGIC;
+
+ *zmgrp = zmgr;
+ return (ISC_R_SUCCESS);
+
+#if 0
+ free_iolock:
+ DESTROYLOCK(&zmgr->iolock);
+#endif
+ free_startuprefreshrl:
+ isc_ratelimiter_detach(&zmgr->startuprefreshrl);
+ free_startupnotifyrl:
+ isc_ratelimiter_detach(&zmgr->startupnotifyrl);
+ free_refreshrl:
+ isc_ratelimiter_detach(&zmgr->refreshrl);
+ free_notifyrl:
+ isc_ratelimiter_detach(&zmgr->notifyrl);
+ free_task:
+ isc_task_detach(&zmgr->task);
+ free_urlock:
+ isc_rwlock_destroy(&zmgr->urlock);
+ 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_createzone(dns_zonemgr_t *zmgr, dns_zone_t **zonep) {
+ isc_result_t result;
+ isc_mem_t *mctx = NULL;
+ dns_zone_t *zone = NULL;
+ void *item;
+
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+ REQUIRE(zonep != NULL && *zonep == NULL);
+
+ if (zmgr->mctxpool == NULL)
+ return (ISC_R_FAILURE);
+
+ item = isc_pool_get(zmgr->mctxpool);
+ if (item == NULL)
+ return (ISC_R_FAILURE);
+
+ isc_mem_attach((isc_mem_t *) item, &mctx);
+ result = dns_zone_create(&zone, mctx);
+ isc_mem_detach(&mctx);
+
+ if (result == ISC_R_SUCCESS)
+ *zonep = zone;
+
+ 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));
+
+ if (zmgr->zonetasks == NULL)
+ return (ISC_R_FAILURE);
+
+ 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, &zone->task);
+ isc_taskpool_gettask(zmgr->loadtasks, &zone->loadtask);
+
+ /*
+ * 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);
+ isc_task_setname(zone->loadtask, "loadzone", 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_tasks;
+
+ /*
+ * 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_tasks:
+ isc_task_detach(&zone->loadtask);
+ 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) {
+ bool free_now = 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 = 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;
+ bool free_now = 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 = true;
+ RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_write);
+
+ if (free_now)
+ zonemgr_free(zmgr);
+ *zmgrp = NULL;
+}
+
+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, 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, true);
+ RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_write);
+}
+
+void
+dns_zonemgr_shutdown(dns_zonemgr_t *zmgr) {
+ dns_zone_t *zone;
+
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+
+ isc_ratelimiter_shutdown(zmgr->notifyrl);
+ isc_ratelimiter_shutdown(zmgr->refreshrl);
+ isc_ratelimiter_shutdown(zmgr->startupnotifyrl);
+ isc_ratelimiter_shutdown(zmgr->startuprefreshrl);
+
+ if (zmgr->task != NULL)
+ isc_task_destroy(&zmgr->task);
+ if (zmgr->zonetasks != NULL)
+ isc_taskpool_destroy(&zmgr->zonetasks);
+ if (zmgr->loadtasks != NULL)
+ isc_taskpool_destroy(&zmgr->loadtasks);
+ if (zmgr->mctxpool != NULL)
+ isc_pool_destroy(&zmgr->mctxpool);
+
+ RWLOCK(&zmgr->rwlock, isc_rwlocktype_read);
+ for (zone = ISC_LIST_HEAD(zmgr->zones);
+ zone != NULL;
+ zone = ISC_LIST_NEXT(zone, link))
+ {
+ LOCK_ZONE(zone);
+ forward_cancel(zone);
+ UNLOCK_ZONE(zone);
+ }
+ RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_read);
+}
+
+static isc_result_t
+mctxinit(void **target, void *arg) {
+ isc_result_t result;
+ isc_mem_t *mctx = NULL;
+
+ UNUSED(arg);
+
+ REQUIRE(target != NULL && *target == NULL);
+
+ result = isc_mem_create(0, 0, &mctx);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ isc_mem_setname(mctx, "zonemgr-pool", NULL);
+
+ *target = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static void
+mctxfree(void **target) {
+ isc_mem_t *mctx = *(isc_mem_t **) target;
+ isc_mem_detach(&mctx);
+ *target = NULL;
+}
+
+#define ZONES_PER_TASK 100
+#define ZONES_PER_MCTX 1000
+
+isc_result_t
+dns_zonemgr_setsize(dns_zonemgr_t *zmgr, int num_zones) {
+ isc_result_t result;
+ int ntasks = num_zones / ZONES_PER_TASK;
+ int nmctx = num_zones / ZONES_PER_MCTX;
+ isc_taskpool_t *pool = NULL;
+ isc_pool_t *mctxpool = NULL;
+
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+
+ /*
+ * For anything fewer than 1000 zones we use 10 tasks in
+ * the task pools. More than that, and we'll scale at one
+ * task per 100 zones. Similarly, for anything smaller than
+ * 2000 zones we use 2 memory contexts, then scale at 1:1000.
+ */
+ if (ntasks < 10)
+ ntasks = 10;
+ if (nmctx < 2)
+ nmctx = 2;
+
+ /* Create or resize the zone task pools. */
+ if (zmgr->zonetasks == NULL)
+ result = isc_taskpool_create(zmgr->taskmgr, zmgr->mctx,
+ ntasks, 2, &pool);
+ else
+ result = isc_taskpool_expand(&zmgr->zonetasks, ntasks, &pool);
+
+ if (result == ISC_R_SUCCESS)
+ zmgr->zonetasks = pool;
+
+ pool = NULL;
+ if (zmgr->loadtasks == NULL)
+ result = isc_taskpool_create(zmgr->taskmgr, zmgr->mctx,
+ ntasks, 2, &pool);
+ else
+ result = isc_taskpool_expand(&zmgr->loadtasks, ntasks, &pool);
+
+ if (result == ISC_R_SUCCESS)
+ zmgr->loadtasks = pool;
+
+ /*
+ * We always set all tasks in the zone-load task pool to
+ * privileged. This prevents other tasks in the system from
+ * running while the server task manager is in privileged
+ * mode.
+ *
+ * NOTE: If we start using task privileges for any other
+ * part of the system than zone tasks, then this will need to be
+ * revisted. In that case we'd want to turn on privileges for
+ * zone tasks only when we were loading, and turn them off the
+ * rest of the time. For now, however, it's okay to just
+ * set it and forget it.
+ */
+ isc_taskpool_setprivilege(zmgr->loadtasks, true);
+
+ /* Create or resize the zone memory context pool. */
+ if (zmgr->mctxpool == NULL)
+ result = isc_pool_create(zmgr->mctx, nmctx, mctxfree,
+ mctxinit, NULL, &mctxpool);
+ else
+ result = isc_pool_expand(&zmgr->mctxpool, nmctx, &mctxpool);
+
+ if (result == ISC_R_SUCCESS)
+ zmgr->mctxpool = mctxpool;
+
+ return (result);
+}
+
+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->notifyrl);
+ isc_ratelimiter_detach(&zmgr->refreshrl);
+ isc_ratelimiter_detach(&zmgr->startupnotifyrl);
+ isc_ratelimiter_detach(&zmgr->startuprefreshrl);
+
+ isc_rwlock_destroy(&zmgr->urlock);
+ 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, uint32_t value) {
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+
+ zmgr->transfersin = value;
+}
+
+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, uint32_t value) {
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+
+ zmgr->transfersperns = value;
+}
+
+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, bool 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;
+ uint32_t nxfrsin, nxfrsperns;
+ dns_zone_t *x;
+ uint32_t maxtransfersin, maxtransfersperns;
+ isc_event_t *e;
+
+ /*
+ * If we are exiting just pretend we got quota so the zone will
+ * be cleaned up in the zone's task context.
+ */
+ LOCK_ZONE(zone);
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_EXITING)) {
+ UNLOCK_ZONE(zone);
+ goto gotquota;
+ }
+
+ /*
+ * 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);
+ UNLOCK_ZONE(zone);
+
+ /*
+ * 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;
+
+ LOCK_ZONE(x);
+ isc_netaddr_fromsockaddr(&xip, &x->masteraddr);
+ UNLOCK_ZONE(x);
+
+ nxfrsin++;
+ if (isc_netaddr_equal(&xip, &masterip))
+ nxfrsperns++;
+ }
+
+ /* Enforce quota. */
+ if (nxfrsin >= maxtransfersin)
+ return (ISC_R_QUOTA);
+
+ if (nxfrsperns >= maxtransfersperns)
+ return (ISC_R_QUOTA);
+
+ gotquota:
+ /*
+ * 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, uint32_t iolimit) {
+
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+ REQUIRE(iolimit > 0);
+
+ zmgr->iolimit = iolimit;
+}
+
+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, bool high,
+ isc_task_t *task, isc_taskaction_t action, void *arg,
+ dns_io_t **iop)
+{
+ dns_io_t *io;
+ bool 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 = (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) {
+ bool send_event = 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 = 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, "unable to load from '%s'; "
+ "renaming file to '%s' for failure analysis and "
+ "retransferring.", path, buf);
+
+ cleanup:
+ isc_mem_put(zone->mctx, buf, buflen);
+}
+
+#if 0
+/* Hook for ondestroy notification 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
+
+static void
+setrl(isc_ratelimiter_t *rl, unsigned int *rate, unsigned int value) {
+ isc_interval_t interval;
+ uint32_t s, ns;
+ uint32_t pertic;
+ isc_result_t result;
+
+ 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(rl, &interval);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ isc_ratelimiter_setpertic(rl, pertic);
+
+ *rate = value;
+}
+
+void
+dns_zonemgr_setnotifyrate(dns_zonemgr_t *zmgr, unsigned int value) {
+
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+
+ setrl(zmgr->notifyrl, &zmgr->notifyrate, value);
+}
+
+void
+dns_zonemgr_setstartupnotifyrate(dns_zonemgr_t *zmgr, unsigned int value) {
+
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+
+ setrl(zmgr->startupnotifyrl, &zmgr->startupnotifyrate, value);
+}
+
+void
+dns_zonemgr_setserialqueryrate(dns_zonemgr_t *zmgr, unsigned int value) {
+
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+
+ setrl(zmgr->refreshrl, &zmgr->serialqueryrate, value);
+ /* XXXMPA seperate out once we have the code to support this. */
+ setrl(zmgr->startuprefreshrl, &zmgr->startupserialqueryrate, value);
+}
+
+unsigned int
+dns_zonemgr_getnotifyrate(dns_zonemgr_t *zmgr) {
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+
+ return (zmgr->notifyrate);
+}
+
+unsigned int
+dns_zonemgr_getstartupnotifyrate(dns_zonemgr_t *zmgr) {
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+
+ return (zmgr->startupnotifyrate);
+}
+
+unsigned int
+dns_zonemgr_getserialqueryrate(dns_zonemgr_t *zmgr) {
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+
+ return (zmgr->serialqueryrate);
+}
+
+bool
+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;
+ uint32_t seconds = isc_time_seconds(now);
+ uint32_t count = 0;
+
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+
+ locktype = isc_rwlocktype_read;
+ RWLOCK(&zmgr->urlock, 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->urlock);
+ if (result == ISC_R_SUCCESS) {
+ locktype = isc_rwlocktype_write;
+ zmgr->unreachable[i].last = seconds;
+ count = zmgr->unreachable[i].count;
+ }
+ break;
+ }
+ }
+ RWUNLOCK(&zmgr->urlock, locktype);
+ return (i < UNREACH_CHACHE_SIZE && count > 1U);
+}
+
+void
+dns_zonemgr_unreachabledel(dns_zonemgr_t *zmgr, isc_sockaddr_t *remote,
+ isc_sockaddr_t *local)
+{
+ unsigned int i;
+ isc_rwlocktype_t locktype;
+ isc_result_t result;
+
+ char master[ISC_SOCKADDR_FORMATSIZE];
+ char source[ISC_SOCKADDR_FORMATSIZE];
+
+ isc_sockaddr_format(remote, master, sizeof(master));
+ isc_sockaddr_format(local, source, sizeof(source));
+
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+
+ locktype = isc_rwlocktype_read;
+ RWLOCK(&zmgr->urlock, locktype);
+ for (i = 0; i < UNREACH_CHACHE_SIZE; i++) {
+ if (isc_sockaddr_equal(&zmgr->unreachable[i].remote, remote) &&
+ isc_sockaddr_equal(&zmgr->unreachable[i].local, local)) {
+ if (zmgr->unreachable[i].expire == 0)
+ break;
+ result = isc_rwlock_tryupgrade(&zmgr->urlock);
+ if (result == ISC_R_SUCCESS) {
+ locktype = isc_rwlocktype_write;
+ zmgr->unreachable[i].expire = 0;
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_ZONE, ISC_LOG_INFO,
+ "master %s (source %s) deleted "
+ "from unreachable cache",
+ master, source);
+ }
+ break;
+ }
+ }
+ RWUNLOCK(&zmgr->urlock, locktype);
+}
+
+void
+dns_zonemgr_unreachableadd(dns_zonemgr_t *zmgr, isc_sockaddr_t *remote,
+ isc_sockaddr_t *local, isc_time_t *now)
+{
+ uint32_t seconds = isc_time_seconds(now);
+ uint32_t last = seconds;
+ unsigned int i, slot = UNREACH_CHACHE_SIZE, oldest = 0;
+
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+
+ RWLOCK(&zmgr->urlock, 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;
+ if (zmgr->unreachable[i].expire < seconds)
+ zmgr->unreachable[i].count = 1;
+ else
+ zmgr->unreachable[i].count++;
+ } 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;
+ zmgr->unreachable[slot].count = 1;
+ } 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;
+ zmgr->unreachable[oldest].count = 1;
+ }
+ RWUNLOCK(&zmgr->urlock, isc_rwlocktype_write);
+}
+
+void
+dns_zone_forcereload(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ if (zone->type == dns_zone_master ||
+ (zone->type == dns_zone_redirect && zone->masters == NULL))
+ return;
+
+ LOCK_ZONE(zone);
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_FORCEXFER);
+ UNLOCK_ZONE(zone);
+ dns_zone_refresh(zone);
+}
+
+bool
+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, bool on) {
+ /*
+ * This function is obsoleted.
+ */
+ UNUSED(zone);
+ UNUSED(on);
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+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, isc_stats_t *stats) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(zone->stats == NULL);
+
+ LOCK_ZONE(zone);
+ zone->stats = NULL;
+ isc_stats_attach(stats, &zone->stats);
+ UNLOCK_ZONE(zone);
+}
+
+void
+dns_zone_setrequeststats(dns_zone_t *zone, isc_stats_t *stats) {
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ if (zone->requeststats_on && stats == NULL)
+ zone->requeststats_on = false;
+ else if (!zone->requeststats_on && stats != NULL) {
+ if (zone->requeststats == NULL) {
+ isc_stats_attach(stats, &zone->requeststats);
+ zone->requeststats_on = true;
+ }
+ }
+ UNLOCK_ZONE(zone);
+}
+
+void
+dns_zone_setrcvquerystats(dns_zone_t *zone, dns_stats_t *stats) {
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ if (zone->requeststats_on && stats != NULL) {
+ if (zone->rcvquerystats == NULL) {
+ dns_stats_attach(stats, &zone->rcvquerystats);
+ zone->requeststats_on = true;
+ }
+ }
+ UNLOCK_ZONE(zone);
+}
+
+isc_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);
+}
+
+/*
+ * Return the received query stats bucket
+ * see note from dns_zone_getrequeststats()
+ */
+dns_stats_t *
+dns_zone_getrcvquerystats(dns_zone_t *zone) {
+ if (zone->requeststats_on)
+ return (zone->rcvquerystats);
+ 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 && zone->masters != NULL &&
+ 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;
+ case DNS_ZONESTATE_AUTOMATIC:
+ 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;
+ if (zone->automatic)
+ 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) {
+ bool ok = true;
+ bool fail = 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) &&
+ rdata->type != dns_rdatatype_nsec3)
+ return (ISC_R_SUCCESS);
+
+ if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKNAMESFAIL) ||
+ rdata->type == dns_rdatatype_nsec3) {
+ level = ISC_LOG_ERROR;
+ fail = true;
+ }
+
+ ok = dns_rdata_checkowner(name, rdata->rdclass, rdata->type, 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, uint32_t delay) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ zone->notifydelay = delay;
+ UNLOCK_ZONE(zone);
+}
+
+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,
+ uint16_t keyid, bool deleteit)
+{
+ 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, deleteit);
+ UNLOCK_ZONE(zone);
+
+ return (result);
+}
+
+/*
+ * Called when a dynamic update for an NSEC3PARAM record is received.
+ *
+ * If set, transform the NSEC3 salt into human-readable form so that it can be
+ * logged. Then call zone_addnsec3chain(), passing NSEC3PARAM RDATA to it.
+ */
+isc_result_t
+dns_zone_addnsec3chain(dns_zone_t *zone, dns_rdata_nsec3param_t *nsec3param) {
+ isc_result_t result;
+ char salt[255*2+1];
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ result = dns_nsec3param_salttotext(nsec3param, salt, sizeof(salt));
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ 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, uint32_t nodes) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ if (nodes == 0)
+ nodes = 1;
+ zone->nodes = nodes;
+}
+
+void
+dns_zone_setsignatures(dns_zone_t *zone, uint32_t signatures) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ /*
+ * We treat signatures as a signed value so explicitly
+ * limit its range here.
+ */
+ if (signatures > INT32_MAX)
+ signatures = INT32_MAX;
+ else if (signatures == 0)
+ signatures = 1;
+ zone->signatures = signatures;
+}
+
+uint32_t
+dns_zone_getsignatures(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ return (zone->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, uint16_t keyid,
+ bool deleteit)
+{
+ dns_signing_t *signing;
+ dns_signing_t *current;
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_time_t now;
+ dns_db_t *db = NULL;
+
+ 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->deleteit = deleteit;
+ signing->done = false;
+
+ TIME_NOW(&now);
+
+ 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) {
+ result = ISC_R_NOTFOUND;
+ goto cleanup;
+ }
+
+ dns_db_attach(db, &signing->db);
+
+ for (current = ISC_LIST_HEAD(zone->signing);
+ current != NULL;
+ current = ISC_LIST_NEXT(current, link)) {
+ if (current->db == signing->db &&
+ current->algorithm == signing->algorithm &&
+ current->keyid == signing->keyid) {
+ if (current->deleteit != signing->deleteit)
+ current->done = true;
+ else
+ goto cleanup;
+ }
+ }
+
+ 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);
+ }
+ }
+
+ cleanup:
+ if (signing != NULL) {
+ if (signing->db != NULL)
+ dns_db_detach(&signing->db);
+ if (signing->dbiterator != NULL)
+ dns_dbiterator_destroy(&signing->dbiterator);
+ isc_mem_put(zone->mctx, signing, sizeof *signing);
+ }
+ if (db != NULL)
+ dns_db_detach(&db);
+ return (result);
+}
+
+static void
+logmsg(const char *format, ...) {
+ va_list args;
+ va_start(args, format);
+ isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_ZONE,
+ ISC_LOG_DEBUG(1), format, args);
+ va_end(args);
+}
+
+static void
+clear_keylist(dns_dnsseckeylist_t *list, isc_mem_t *mctx) {
+ dns_dnsseckey_t *key;
+ while (!ISC_LIST_EMPTY(*list)) {
+ key = ISC_LIST_HEAD(*list);
+ ISC_LIST_UNLINK(*list, key, link);
+ dns_dnsseckey_destroy(mctx, &key);
+ }
+}
+
+/* Called once; *timep should be set to the current time. */
+static isc_result_t
+next_keyevent(dst_key_t *key, isc_stdtime_t *timep) {
+ isc_result_t result;
+ isc_stdtime_t now, then = 0, event;
+ int i;
+
+ now = *timep;
+
+ for (i = 0; i <= DST_MAX_TIMES; i++) {
+ result = dst_key_gettime(key, i, &event);
+ if (result == ISC_R_SUCCESS && event > now &&
+ (then == 0 || event < then))
+ then = event;
+ }
+
+ if (then != 0) {
+ *timep = then;
+ return (ISC_R_SUCCESS);
+ }
+
+ return (ISC_R_NOTFOUND);
+}
+
+static isc_result_t
+rr_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
+ const dns_rdata_t *rdata, bool *flag)
+{
+ dns_rdataset_t rdataset;
+ dns_dbnode_t *node = NULL;
+ isc_result_t result;
+
+ dns_rdataset_init(&rdataset);
+ if (rdata->type == dns_rdatatype_nsec3)
+ CHECK(dns_db_findnsec3node(db, name, false, &node));
+ else
+ CHECK(dns_db_findnode(db, name, false, &node));
+ result = dns_db_findrdataset(db, node, ver, rdata->type, 0,
+ (isc_stdtime_t) 0, &rdataset, NULL);
+ if (result == ISC_R_NOTFOUND) {
+ *flag = false;
+ result = ISC_R_SUCCESS;
+ goto failure;
+ }
+
+ for (result = dns_rdataset_first(&rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rdataset)) {
+ dns_rdata_t myrdata = DNS_RDATA_INIT;
+ dns_rdataset_current(&rdataset, &myrdata);
+ if (!dns_rdata_compare(&myrdata, rdata))
+ break;
+ }
+ dns_rdataset_disassociate(&rdataset);
+ if (result == ISC_R_SUCCESS) {
+ *flag = true;
+ } else if (result == ISC_R_NOMORE) {
+ *flag = false;
+ result = ISC_R_SUCCESS;
+ }
+
+ failure:
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ return (result);
+}
+
+/*
+ * Add records to signal the state of signing or of key removal.
+ */
+static isc_result_t
+add_signing_records(dns_db_t *db, dns_rdatatype_t privatetype,
+ dns_dbversion_t *ver, dns_diff_t *diff,
+ bool sign_all)
+{
+ dns_difftuple_t *tuple, *newtuple = NULL;
+ dns_rdata_dnskey_t dnskey;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ bool flag;
+ isc_region_t r;
+ isc_result_t result = ISC_R_SUCCESS;
+ uint16_t keyid;
+ unsigned char buf[5];
+ dns_name_t *name = dns_db_origin(db);
+
+ for (tuple = ISC_LIST_HEAD(diff->tuples);
+ tuple != NULL;
+ tuple = ISC_LIST_NEXT(tuple, link)) {
+ if (tuple->rdata.type != dns_rdatatype_dnskey)
+ continue;
+
+ result = dns_rdata_tostruct(&tuple->rdata, &dnskey, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ if ((dnskey.flags &
+ (DNS_KEYFLAG_OWNERMASK|DNS_KEYTYPE_NOAUTH))
+ != DNS_KEYOWNER_ZONE)
+ continue;
+
+ dns_rdata_toregion(&tuple->rdata, &r);
+
+ keyid = dst_region_computeid(&r, dnskey.algorithm);
+
+ buf[0] = dnskey.algorithm;
+ buf[1] = (keyid & 0xff00) >> 8;
+ buf[2] = (keyid & 0xff);
+ buf[3] = (tuple->op == DNS_DIFFOP_ADD) ? 0 : 1;
+ buf[4] = 0;
+ rdata.data = buf;
+ rdata.length = sizeof(buf);
+ rdata.type = privatetype;
+ rdata.rdclass = tuple->rdata.rdclass;
+
+ if (sign_all || tuple->op == DNS_DIFFOP_DEL) {
+ CHECK(rr_exists(db, ver, name, &rdata, &flag));
+ if (flag)
+ continue;
+ CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD,
+ name, 0, &rdata, &newtuple));
+ CHECK(do_one_tuple(&newtuple, db, ver, diff));
+ INSIST(newtuple == NULL);
+ }
+
+ /*
+ * Remove any record which says this operation has already
+ * completed.
+ */
+ buf[4] = 1;
+ CHECK(rr_exists(db, ver, name, &rdata, &flag));
+ if (flag) {
+ CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_DEL,
+ name, 0, &rdata, &newtuple));
+ CHECK(do_one_tuple(&newtuple, db, ver, diff));
+ INSIST(newtuple == NULL);
+ }
+ }
+ failure:
+ return (result);
+}
+
+static isc_result_t
+sign_apex(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver,
+ isc_stdtime_t now, dns_diff_t *diff, dns__zonediff_t *zonediff)
+{
+ isc_result_t result;
+ isc_stdtime_t inception, soaexpire;
+ bool check_ksk, keyset_kskonly;
+ dst_key_t *zone_keys[DNS_MAXZONEKEYS];
+ unsigned int nkeys = 0, i;
+ dns_difftuple_t *tuple;
+
+ result = dns__zone_findkeys(zone, db, ver, now, zone->mctx,
+ DNS_MAXZONEKEYS, zone_keys, &nkeys);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "sign_apex:dns__zone_findkeys -> %s",
+ dns_result_totext(result));
+ return (result);
+ }
+
+ inception = now - 3600; /* Allow for clock skew. */
+ soaexpire = now + dns_zone_getsigvalidityinterval(zone);
+
+ check_ksk = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_UPDATECHECKKSK);
+ keyset_kskonly = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_DNSKEYKSKONLY);
+
+ /*
+ * See if dns__zone_updatesigs() will update DNSKEY signature and if
+ * not cause them to sign so that newly activated keys are used.
+ */
+ for (tuple = ISC_LIST_HEAD(diff->tuples);
+ tuple != NULL;
+ tuple = ISC_LIST_NEXT(tuple, link)) {
+ if (tuple->rdata.type == dns_rdatatype_dnskey &&
+ dns_name_equal(&tuple->name, &zone->origin))
+ break;
+ }
+
+ if (tuple == NULL) {
+ result = del_sigs(zone, db, ver, &zone->origin,
+ dns_rdatatype_dnskey, zonediff,
+ zone_keys, nkeys, now, false);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "sign_apex:del_sigs -> %s",
+ dns_result_totext(result));
+ goto failure;
+ }
+ result = add_sigs(db, ver, &zone->origin, dns_rdatatype_dnskey,
+ zonediff->diff, zone_keys, nkeys, zone->mctx,
+ inception, soaexpire, check_ksk,
+ keyset_kskonly);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "sign_apex:add_sigs -> %s",
+ dns_result_totext(result));
+ goto failure;
+ }
+ }
+
+ result = dns__zone_updatesigs(diff, db, ver, zone_keys, nkeys, zone,
+ inception, soaexpire, now, check_ksk,
+ keyset_kskonly, zonediff);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "sign_apex:dns__zone_updatesigs -> %s",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ failure:
+ for (i = 0; i < nkeys; i++)
+ dst_key_free(&zone_keys[i]);
+ return (result);
+}
+
+/*
+ * Prevent the zone entering a inconsistent state where
+ * NSEC only DNSKEYs are present with NSEC3 chains.
+ * See update.c:check_dnssec()
+ */
+static bool
+dnskey_sane(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver,
+ dns_diff_t *diff)
+{
+ isc_result_t result;
+ dns_difftuple_t *tuple;
+ bool nseconly = false, nsec3 = false;
+ dns_rdatatype_t privatetype = dns_zone_getprivatetype(zone);
+
+ /* Scan the tuples for an NSEC-only DNSKEY */
+ for (tuple = ISC_LIST_HEAD(diff->tuples);
+ tuple != NULL;
+ tuple = ISC_LIST_NEXT(tuple, link)) {
+ uint8_t alg;
+ if (tuple->rdata.type != dns_rdatatype_dnskey ||
+ tuple->op != DNS_DIFFOP_ADD)
+ continue;
+
+ alg = tuple->rdata.data[3];
+ if (alg == DST_ALG_RSAMD5 || alg == DST_ALG_RSASHA1 ||
+ alg == DST_ALG_DSA || alg == DST_ALG_ECC) {
+ nseconly = true;
+ break;
+ }
+ }
+
+ /* Check existing DB for NSEC-only DNSKEY */
+ if (!nseconly) {
+ result = dns_nsec_nseconly(db, ver, &nseconly);
+ if (result == ISC_R_NOTFOUND)
+ result = ISC_R_SUCCESS;
+ CHECK(result);
+ }
+
+ /* Check existing DB for NSEC3 */
+ if (!nsec3)
+ CHECK(dns_nsec3_activex(db, ver, false,
+ privatetype, &nsec3));
+
+ /* Refuse to allow NSEC3 with NSEC-only keys */
+ if (nseconly && nsec3) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "NSEC only DNSKEYs and NSEC3 chains not allowed");
+ goto failure;
+ }
+
+ return (true);
+
+ failure:
+ return (false);
+}
+
+static isc_result_t
+clean_nsec3param(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver,
+ dns_diff_t *diff)
+{
+ isc_result_t result;
+ dns_dbnode_t *node = NULL;
+ dns_rdataset_t rdataset;
+
+ dns_rdataset_init(&rdataset);
+ CHECK(dns_db_getoriginnode(db, &node));
+
+ result = dns_db_findrdataset(db, node, ver, dns_rdatatype_dnskey,
+ dns_rdatatype_none, 0, &rdataset, NULL);
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ if (result != ISC_R_NOTFOUND)
+ goto failure;
+
+ result = dns_nsec3param_deletechains(db, ver, zone, true, diff);
+
+ failure:
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ return (result);
+}
+
+/*
+ * Given an RRSIG rdataset and an algorithm, determine whether there
+ * are any signatures using that algorithm.
+ */
+static bool
+signed_with_alg(dns_rdataset_t *rdataset, dns_secalg_t alg) {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_rrsig_t rrsig;
+ isc_result_t result;
+
+ REQUIRE(rdataset == NULL || rdataset->type == dns_rdatatype_rrsig);
+ if (rdataset == NULL || !dns_rdataset_isassociated(rdataset)) {
+ return (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);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ dns_rdata_reset(&rdata);
+ if (rrsig.algorithm == alg)
+ return (true);
+ }
+
+ return (false);
+}
+
+static isc_result_t
+add_chains(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver,
+ dns_diff_t *diff)
+{
+ dns_name_t *origin;
+ bool build_nsec3;
+ isc_result_t result;
+
+ origin = dns_db_origin(db);
+ CHECK(dns_private_chains(db, ver, zone->privatetype, NULL,
+ &build_nsec3));
+ if (build_nsec3)
+ CHECK(dns_nsec3_addnsec3sx(db, ver, origin, zone->minimum,
+ false, zone->privatetype, diff));
+ CHECK(updatesecure(db, ver, origin, zone->minimum, true, diff));
+
+ failure:
+ return (result);
+}
+
+static void
+zone_rekey(dns_zone_t *zone) {
+ isc_result_t result;
+ dns_db_t *db = NULL;
+ dns_dbnode_t *node = NULL;
+ dns_dbversion_t *ver = NULL;
+ dns_rdataset_t cdsset, soaset, soasigs, keyset, keysigs, cdnskeyset;
+ dns_dnsseckeylist_t dnskeys, keys, rmkeys;
+ dns_dnsseckey_t *key;
+ dns_diff_t diff, _sig_diff;
+ dns__zonediff_t zonediff;
+ bool commit = false, newactive = false;
+ bool newalg = false;
+ bool fullsign;
+ dns_ttl_t ttl = 3600;
+ const char *dir;
+ isc_mem_t *mctx;
+ isc_stdtime_t now;
+ isc_time_t timenow;
+ isc_interval_t ival;
+ char timebuf[80];
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ ISC_LIST_INIT(dnskeys);
+ ISC_LIST_INIT(keys);
+ ISC_LIST_INIT(rmkeys);
+ dns_rdataset_init(&soaset);
+ dns_rdataset_init(&soasigs);
+ dns_rdataset_init(&keyset);
+ dns_rdataset_init(&keysigs);
+ dns_rdataset_init(&cdsset);
+ dns_rdataset_init(&cdnskeyset);
+ dir = dns_zone_getkeydirectory(zone);
+ mctx = zone->mctx;
+ dns_diff_init(mctx, &diff);
+ dns_diff_init(mctx, &_sig_diff);
+ zonediff_init(&zonediff, &_sig_diff);
+
+ CHECK(dns_zone_getdb(zone, &db));
+ CHECK(dns_db_newversion(db, &ver));
+ CHECK(dns_db_getoriginnode(db, &node));
+
+ TIME_NOW(&timenow);
+ now = isc_time_seconds(&timenow);
+
+ dns_zone_log(zone, ISC_LOG_INFO, "reconfiguring zone keys");
+
+ /* Get the SOA record's TTL */
+ CHECK(dns_db_findrdataset(db, node, ver, dns_rdatatype_soa,
+ dns_rdatatype_none, 0, &soaset, &soasigs));
+ ttl = soaset.ttl;
+ dns_rdataset_disassociate(&soaset);
+
+ /* Get the DNSKEY rdataset */
+ result = dns_db_findrdataset(db, node, ver, dns_rdatatype_dnskey,
+ dns_rdatatype_none, 0, &keyset, &keysigs);
+ if (result == ISC_R_SUCCESS) {
+ ttl = keyset.ttl;
+ CHECK(dns_dnssec_keylistfromrdataset(&zone->origin, dir,
+ mctx, &keyset,
+ &keysigs, &soasigs,
+ false, false,
+ &dnskeys));
+ } else if (result != ISC_R_NOTFOUND)
+ goto failure;
+
+
+ /* Get the CDS rdataset */
+ result = dns_db_findrdataset(db, node, ver, dns_rdatatype_cds,
+ dns_rdatatype_none, 0, &cdsset, NULL);
+ if (result != ISC_R_SUCCESS && dns_rdataset_isassociated(&cdsset))
+ dns_rdataset_disassociate(&cdsset);
+
+ /* Get the CDNSKEY rdataset */
+ result = dns_db_findrdataset(db, node, ver, dns_rdatatype_cdnskey,
+ dns_rdatatype_none, 0, &cdnskeyset, NULL);
+ if (result != ISC_R_SUCCESS && dns_rdataset_isassociated(&cdnskeyset))
+ dns_rdataset_disassociate(&cdnskeyset);
+
+ /*
+ * True when called from "rndc sign". Indicates the zone should be
+ * fully signed now.
+ */
+ fullsign = DNS_ZONEKEY_OPTION(zone, DNS_ZONEKEY_FULLSIGN);
+
+ result = dns_dnssec_findmatchingkeys2(&zone->origin, dir, now, mctx,
+ &keys);
+ if (result == ISC_R_SUCCESS) {
+ bool check_ksk;
+ check_ksk = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_UPDATECHECKKSK);
+
+ result = dns_dnssec_updatekeys(&dnskeys, &keys, &rmkeys,
+ &zone->origin, ttl, &diff,
+ !check_ksk,
+ mctx, logmsg);
+ /*
+ * Keys couldn't be updated for some reason;
+ * try again later.
+ */
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR, "zone_rekey:"
+ "couldn't update zone keys: %s",
+ isc_result_totext(result));
+ goto failure;
+ }
+
+ /*
+ * Update CDS / CDNSKEY records.
+ */
+ result = dns_dnssec_syncupdate(&dnskeys, &rmkeys, &cdsset,
+ &cdnskeyset, now, ttl,
+ &diff, mctx);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR, "zone_rekey:"
+ "couldn't update CDS/CDNSKEY: %s",
+ isc_result_totext(result));
+ goto failure;
+ }
+
+ /*
+ * See if any pre-existing keys have newly become active;
+ * also, see if any new key is for a new algorithm, as in that
+ * event, we need to sign the zone fully. (If there's a new
+ * key, but it's for an already-existing algorithm, then
+ * the zone signing can be handled incrementally.)
+ */
+ for (key = ISC_LIST_HEAD(dnskeys);
+ key != NULL;
+ key = ISC_LIST_NEXT(key, link)) {
+ if (!key->first_sign)
+ continue;
+
+ newactive = true;
+
+ if (!dns_rdataset_isassociated(&keysigs)) {
+ newalg = true;
+ break;
+ }
+
+ if (signed_with_alg(&keysigs, dst_key_alg(key->key))) {
+ /*
+ * This isn't a new algorithm; clear
+ * first_sign so we won't sign the
+ * whole zone with this key later
+ */
+ key->first_sign = false;
+ } else {
+ newalg = true;
+ break;
+ }
+ }
+
+ if ((newactive || fullsign || !ISC_LIST_EMPTY(diff.tuples)) &&
+ dnskey_sane(zone, db, ver, &diff)) {
+ CHECK(dns_diff_apply(&diff, db, ver));
+ CHECK(clean_nsec3param(zone, db, ver, &diff));
+ CHECK(add_signing_records(db, zone->privatetype,
+ ver, &diff,
+ (newalg || fullsign)));
+ CHECK(update_soa_serial(db, ver, &diff, mctx,
+ zone->updatemethod));
+ CHECK(add_chains(zone, db, ver, &diff));
+ CHECK(sign_apex(zone, db, ver, now, &diff, &zonediff));
+ CHECK(zone_journal(zone, zonediff.diff, NULL,
+ "zone_rekey"));
+ commit = true;
+ }
+ }
+
+ dns_db_closeversion(db, &ver, true);
+
+ if (commit) {
+ dns_difftuple_t *tuple;
+
+ LOCK_ZONE(zone);
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NEEDNOTIFY);
+
+ zone_needdump(zone, DNS_DUMP_DELAY);
+
+ zone_settimer(zone, &timenow);
+
+ /* Remove any signatures from removed keys. */
+ if (!ISC_LIST_EMPTY(rmkeys)) {
+ for (key = ISC_LIST_HEAD(rmkeys);
+ key != NULL;
+ key = ISC_LIST_NEXT(key, link)) {
+ result = zone_signwithkey(zone,
+ dst_key_alg(key->key),
+ dst_key_id(key->key),
+ true);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_signwithkey failed: %s",
+ dns_result_totext(result));
+ }
+ }
+ }
+
+ if (fullsign) {
+ /*
+ * "rndc sign" was called, so we now sign the zone
+ * with all active keys, whether they're new or not.
+ */
+ for (key = ISC_LIST_HEAD(dnskeys);
+ key != NULL;
+ key = ISC_LIST_NEXT(key, link)) {
+ if (!key->force_sign && !key->hint_sign)
+ continue;
+
+ result = zone_signwithkey(zone,
+ dst_key_alg(key->key),
+ dst_key_id(key->key),
+ false);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_signwithkey failed: %s",
+ dns_result_totext(result));
+ }
+ }
+ } else if (newalg) {
+ /*
+ * We haven't been told to sign fully, but a new
+ * algorithm was added to the DNSKEY. We sign
+ * the full zone, but only with newly active
+ * keys.
+ */
+ for (key = ISC_LIST_HEAD(dnskeys);
+ key != NULL;
+ key = ISC_LIST_NEXT(key, link)) {
+ if (!key->first_sign)
+ continue;
+
+ result = zone_signwithkey(zone,
+ dst_key_alg(key->key),
+ dst_key_id(key->key),
+ false);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_signwithkey failed: %s",
+ dns_result_totext(result));
+ }
+ }
+ }
+
+ /*
+ * Clear fullsign flag, if it was set, so we don't do
+ * another full signing next time
+ */
+ zone->keyopts &= ~DNS_ZONEKEY_FULLSIGN;
+
+ /*
+ * Cause the zone to add/delete NSEC3 chains for the
+ * deferred NSEC3PARAM changes.
+ */
+ for (tuple = ISC_LIST_HEAD(zonediff.diff->tuples);
+ tuple != NULL;
+ tuple = ISC_LIST_NEXT(tuple, link)) {
+ unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE];
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_nsec3param_t nsec3param;
+
+ if (tuple->rdata.type != zone->privatetype ||
+ tuple->op != DNS_DIFFOP_ADD)
+ continue;
+
+ if (!dns_nsec3param_fromprivate(&tuple->rdata, &rdata,
+ buf, sizeof(buf)))
+ continue;
+ result = dns_rdata_tostruct(&rdata, &nsec3param, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ if (nsec3param.flags == 0)
+ continue;
+
+ 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));
+ }
+ }
+
+ /*
+ * Activate any NSEC3 chain updates that may have
+ * been scheduled before this rekey.
+ */
+ if (fullsign || newalg)
+ resume_addnsec3chain(zone);
+
+ /*
+ * Schedule the next resigning event
+ */
+ set_resigntime(zone);
+ UNLOCK_ZONE(zone);
+ }
+
+ isc_time_settoepoch(&zone->refreshkeytime);
+
+ /*
+ * If we're doing key maintenance, set the key refresh timer to
+ * the next scheduled key event or to 'dnssec-loadkeys-interval'
+ * seconds in the future, whichever is sooner.
+ */
+ if (DNS_ZONEKEY_OPTION(zone, DNS_ZONEKEY_MAINTAIN)) {
+ isc_time_t timethen;
+ isc_stdtime_t then;
+
+ LOCK_ZONE(zone);
+ DNS_ZONE_TIME_ADD(&timenow, zone->refreshkeyinterval,
+ &timethen);
+ zone->refreshkeytime = timethen;
+ UNLOCK_ZONE(zone);
+
+ for (key = ISC_LIST_HEAD(dnskeys);
+ key != NULL;
+ key = ISC_LIST_NEXT(key, link)) {
+ then = now;
+ result = next_keyevent(key->key, &then);
+ if (result != ISC_R_SUCCESS)
+ continue;
+
+ DNS_ZONE_TIME_ADD(&timenow, then - now, &timethen);
+ LOCK_ZONE(zone);
+ if (isc_time_compare(&timethen,
+ &zone->refreshkeytime) < 0) {
+ zone->refreshkeytime = timethen;
+ }
+ UNLOCK_ZONE(zone);
+ }
+
+ zone_settimer(zone, &timenow);
+
+ isc_time_formattimestamp(&zone->refreshkeytime, timebuf, 80);
+ dns_zone_log(zone, ISC_LOG_INFO, "next key event: %s", timebuf);
+ }
+
+ done:
+ dns_diff_clear(&diff);
+ dns_diff_clear(&_sig_diff);
+
+ clear_keylist(&dnskeys, mctx);
+ clear_keylist(&keys, mctx);
+ clear_keylist(&rmkeys, mctx);
+
+ if (ver != NULL)
+ dns_db_closeversion(db, &ver, false);
+ if (dns_rdataset_isassociated(&cdsset))
+ dns_rdataset_disassociate(&cdsset);
+ if (dns_rdataset_isassociated(&keyset))
+ dns_rdataset_disassociate(&keyset);
+ if (dns_rdataset_isassociated(&keysigs))
+ dns_rdataset_disassociate(&keysigs);
+ if (dns_rdataset_isassociated(&soasigs))
+ dns_rdataset_disassociate(&soasigs);
+ if (dns_rdataset_isassociated(&cdnskeyset))
+ dns_rdataset_disassociate(&cdnskeyset);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ if (db != NULL)
+ dns_db_detach(&db);
+
+ INSIST(ver == NULL);
+ return;
+
+ failure:
+ /*
+ * Something went wrong; try again in ten minutes or
+ * after a key refresh interval, whichever is shorter.
+ */
+ isc_interval_set(&ival, ISC_MIN(zone->refreshkeyinterval, 600), 0);
+ isc_time_nowplusinterval(&zone->refreshkeytime, &ival);
+ goto done;
+}
+
+void
+dns_zone_rekey(dns_zone_t *zone, bool fullsign) {
+ isc_time_t now;
+
+ if (zone->type == dns_zone_master && zone->task != NULL) {
+ LOCK_ZONE(zone);
+
+ if (fullsign)
+ zone->keyopts |= DNS_ZONEKEY_FULLSIGN;
+
+ TIME_NOW(&now);
+ zone->refreshkeytime = now;
+ zone_settimer(zone, &now);
+
+ UNLOCK_ZONE(zone);
+ }
+}
+
+isc_result_t
+dns_zone_nscheck(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version,
+ unsigned int *errors)
+{
+ isc_result_t result;
+ dns_dbnode_t *node = NULL;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(errors != NULL);
+
+ result = dns_db_getoriginnode(db, &node);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ result = zone_count_ns_rr(zone, db, node, version, NULL, errors,
+ false);
+ dns_db_detachnode(db, &node);
+ return (result);
+}
+
+isc_result_t
+dns_zone_cdscheck(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version) {
+ isc_result_t result;
+ dns_dbnode_t *node = NULL;
+ dns_rdataset_t dnskey, cds, cdnskey;
+ unsigned char buffer[DNS_DS_BUFFERSIZE];
+ unsigned char algorithms[256];
+ unsigned int i;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ result = dns_db_getoriginnode(db, &node);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ dns_rdataset_init(&cds);
+ dns_rdataset_init(&dnskey);
+ dns_rdataset_init(&cdnskey);
+
+ result = dns_db_findrdataset(db, node, version, dns_rdatatype_cds,
+ dns_rdatatype_none, 0, &cds, NULL);
+ if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND)
+ goto failure;
+
+ result = dns_db_findrdataset(db, node, version, dns_rdatatype_cdnskey,
+ dns_rdatatype_none, 0, &cdnskey, NULL);
+ if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND)
+ goto failure;
+
+ if (!dns_rdataset_isassociated(&cds) &&
+ !dns_rdataset_isassociated(&cdnskey)) {
+ result = ISC_R_SUCCESS;
+ goto failure;
+ }
+
+ result = dns_db_findrdataset(db, node, version, dns_rdatatype_dnskey,
+ dns_rdatatype_none, 0, &dnskey, NULL);
+ if (result == ISC_R_NOTFOUND) {
+ if (dns_rdataset_isassociated(&cds))
+ result = DNS_R_BADCDS;
+ else
+ result = DNS_R_BADCDNSKEY;
+ goto failure;
+ }
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ /*
+ * For each DNSSEC algorithm in the CDS RRset there must be
+ * a matching DNSKEY record.
+ */
+ if (dns_rdataset_isassociated(&cds)) {
+ memset(algorithms, 0, sizeof(algorithms));
+ for (result = dns_rdataset_first(&cds);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&cds)) {
+ dns_rdata_t crdata = DNS_RDATA_INIT;
+ dns_rdata_cds_t structcds;
+
+ dns_rdataset_current(&cds, &crdata);
+ CHECK(dns_rdata_tostruct(&crdata, &structcds, NULL));
+ if (algorithms[structcds.algorithm] == 0)
+ algorithms[structcds.algorithm] = 1;
+ for (result = dns_rdataset_first(&dnskey);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&dnskey)) {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_t dsrdata = DNS_RDATA_INIT;
+
+ dns_rdataset_current(&dnskey, &rdata);
+ CHECK(dns_ds_buildrdata(&zone->origin, &rdata,
+ structcds.digest_type,
+ buffer, &dsrdata));
+ if (crdata.length == dsrdata.length &&
+ memcmp(crdata.data, dsrdata.data,
+ dsrdata.length) == 0) {
+ algorithms[structcds.algorithm] = 2;
+ }
+ }
+ if (result != ISC_R_NOMORE)
+ goto failure;
+ }
+ for (i = 0; i < sizeof(algorithms); i++) {
+ if (algorithms[i] == 1) {
+ result = DNS_R_BADCDNSKEY;
+ goto failure;
+ }
+ }
+ }
+
+ /*
+ * For each DNSSEC algorithm in the CDNSKEY RRset there must be
+ * a matching DNSKEY record.
+ */
+ if (dns_rdataset_isassociated(&cdnskey)) {
+ memset(algorithms, 0, sizeof(algorithms));
+ for (result = dns_rdataset_first(&cdnskey);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&cdnskey)) {
+ dns_rdata_t crdata = DNS_RDATA_INIT;
+ dns_rdata_cdnskey_t structcdnskey;
+
+ dns_rdataset_current(&cdnskey, &crdata);
+ CHECK(dns_rdata_tostruct(&crdata, &structcdnskey,
+ NULL));
+ if (algorithms[structcdnskey.algorithm] == 0)
+ algorithms[structcdnskey.algorithm] = 1;
+ for (result = dns_rdataset_first(&dnskey);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&dnskey)) {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+
+ dns_rdataset_current(&dnskey, &rdata);
+ if (crdata.length == rdata.length &&
+ memcmp(crdata.data, rdata.data,
+ rdata.length) == 0) {
+ algorithms[structcdnskey.algorithm] = 2;
+ }
+ }
+ if (result != ISC_R_NOMORE)
+ goto failure;
+ }
+ for (i = 0; i < sizeof(algorithms); i++) {
+ if (algorithms[i] == 1) {
+ result = DNS_R_BADCDS;
+ goto failure;
+ }
+ }
+ }
+ result = ISC_R_SUCCESS;
+
+ failure:
+ if (dns_rdataset_isassociated(&cds))
+ dns_rdataset_disassociate(&cds);
+ if (dns_rdataset_isassociated(&dnskey))
+ dns_rdataset_disassociate(&dnskey);
+ if (dns_rdataset_isassociated(&cdnskey))
+ dns_rdataset_disassociate(&cdnskey);
+ dns_db_detachnode(db, &node);
+ return (result);
+}
+
+void
+dns_zone_setautomatic(dns_zone_t *zone, bool automatic) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ zone->automatic = automatic;
+ UNLOCK_ZONE(zone);
+}
+
+bool
+dns_zone_getautomatic(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ return (zone->automatic);
+}
+
+void
+dns_zone_setadded(dns_zone_t *zone, bool added) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ zone->added = added;
+ UNLOCK_ZONE(zone);
+}
+
+bool
+dns_zone_getadded(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ return (zone->added);
+}
+
+isc_result_t
+dns_zone_dlzpostload(dns_zone_t *zone, dns_db_t *db)
+{
+ isc_time_t loadtime;
+ isc_result_t result;
+ dns_zone_t *secure = NULL;
+
+ TIME_NOW(&loadtime);
+
+ /*
+ * Lock hierarchy: zmgr, zone, raw.
+ */
+ again:
+ LOCK_ZONE(zone);
+ INSIST(zone != zone->raw);
+ if (inline_secure(zone))
+ LOCK_ZONE(zone->raw);
+ else if (inline_raw(zone)) {
+ secure = zone->secure;
+ TRYLOCK_ZONE(result, secure);
+ if (result != ISC_R_SUCCESS) {
+ UNLOCK_ZONE(zone);
+ secure = NULL;
+#if ISC_PLATFORM_USETHREADS
+ isc_thread_yield();
+#endif
+ goto again;
+ }
+ }
+ result = zone_postload(zone, db, loadtime, ISC_R_SUCCESS);
+ if (inline_secure(zone))
+ UNLOCK_ZONE(zone->raw);
+ else if (secure != NULL)
+ UNLOCK_ZONE(secure);
+ UNLOCK_ZONE(zone);
+ return result;
+}
+
+isc_result_t
+dns_zone_setrefreshkeyinterval(dns_zone_t *zone, uint32_t interval) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ if (interval == 0)
+ return (ISC_R_RANGE);
+ /* Maximum value: 24 hours (3600 minutes) */
+ if (interval > (24 * 60))
+ interval = (24 * 60);
+ /* Multiply by 60 for seconds */
+ zone->refreshkeyinterval = interval * 60;
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_zone_setrequestixfr(dns_zone_t *zone, bool flag) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ zone->requestixfr = flag;
+}
+
+bool
+dns_zone_getrequestixfr(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ return (zone->requestixfr);
+}
+
+void
+dns_zone_setrequestexpire(dns_zone_t *zone, bool flag) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ zone->requestexpire = flag;
+}
+
+bool
+dns_zone_getrequestexpire(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ return (zone->requestexpire);
+}
+
+void
+dns_zone_setserialupdatemethod(dns_zone_t *zone, dns_updatemethod_t method) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ zone->updatemethod = method;
+}
+
+dns_updatemethod_t
+dns_zone_getserialupdatemethod(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ return(zone->updatemethod);
+}
+
+/*
+ * Lock hierarchy: zmgr, zone, raw.
+ */
+isc_result_t
+dns_zone_link(dns_zone_t *zone, dns_zone_t *raw) {
+ isc_result_t result;
+ dns_zonemgr_t *zmgr;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(zone->zmgr != NULL);
+ REQUIRE(zone->task != NULL);
+ REQUIRE(zone->loadtask != NULL);
+ REQUIRE(zone->raw == NULL);
+
+ REQUIRE(DNS_ZONE_VALID(raw));
+ REQUIRE(raw->zmgr == NULL);
+ REQUIRE(raw->task == NULL);
+ REQUIRE(raw->loadtask == NULL);
+ REQUIRE(raw->secure == NULL);
+
+ REQUIRE(zone != raw);
+
+ /*
+ * Lock hierarchy: zmgr, zone, raw.
+ */
+ zmgr = zone->zmgr;
+ RWLOCK(&zmgr->rwlock, isc_rwlocktype_write);
+ LOCK_ZONE(zone);
+ LOCK_ZONE(raw);
+
+ result = isc_timer_create(zmgr->timermgr, isc_timertype_inactive,
+ NULL, NULL, zone->task, zone_timer, raw,
+ &raw->timer);
+ if (result != ISC_R_SUCCESS)
+ goto unlock;
+
+ /*
+ * The timer "holds" a iref.
+ */
+ raw->irefs++;
+ INSIST(raw->irefs != 0);
+
+
+ /* dns_zone_attach(raw, &zone->raw); */
+ isc_refcount_increment(&raw->erefs, NULL);
+ zone->raw = raw;
+
+ /* dns_zone_iattach(zone, &raw->secure); */
+ zone_iattach(zone, &raw->secure);
+
+ isc_task_attach(zone->task, &raw->task);
+ isc_task_attach(zone->loadtask, &raw->loadtask);
+
+ ISC_LIST_APPEND(zmgr->zones, raw, link);
+ raw->zmgr = zmgr;
+ zmgr->refs++;
+
+ unlock:
+ UNLOCK_ZONE(raw);
+ UNLOCK_ZONE(zone);
+ RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_write);
+ return (result);
+}
+
+void
+dns_zone_getraw(dns_zone_t *zone, dns_zone_t **raw) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(raw != NULL && *raw == NULL);
+
+ LOCK(&zone->lock);
+ INSIST(zone != zone->raw);
+ if (zone->raw != NULL)
+ dns_zone_attach(zone->raw, raw);
+ UNLOCK(&zone->lock);
+}
+
+struct keydone {
+ isc_event_t event;
+ bool all;
+ unsigned char data[5];
+};
+
+#define PENDINGFLAGS (DNS_NSEC3FLAG_CREATE|DNS_NSEC3FLAG_INITIAL)
+
+static void
+keydone(isc_task_t *task, isc_event_t *event) {
+ const char *me = "keydone";
+ bool commit = false;
+ isc_result_t result;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_dbversion_t *oldver = NULL, *newver = NULL;
+ dns_zone_t *zone;
+ dns_db_t *db = NULL;
+ dns_dbnode_t *node = NULL;
+ dns_rdataset_t rdataset;
+ dns_diff_t diff;
+ struct keydone *kd = (struct keydone *)event;
+ dns_update_log_t log = { update_log_cb, NULL };
+ bool clear_pending = false;
+
+ UNUSED(task);
+
+ zone = event->ev_arg;
+ INSIST(DNS_ZONE_VALID(zone));
+
+ ENTER;
+
+ dns_rdataset_init(&rdataset);
+ dns_diff_init(zone->mctx, &diff);
+
+ 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)
+ goto failure;
+
+ dns_db_currentversion(db, &oldver);
+ result = dns_db_newversion(db, &newver);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "keydone:dns_db_newversion -> %s",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ result = dns_db_getoriginnode(db, &node);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ result = dns_db_findrdataset(db, node, newver, zone->privatetype,
+ dns_rdatatype_none, 0, &rdataset, NULL);
+ if (result == ISC_R_NOTFOUND) {
+ INSIST(!dns_rdataset_isassociated(&rdataset));
+ goto failure;
+ }
+ if (result != ISC_R_SUCCESS) {
+ INSIST(!dns_rdataset_isassociated(&rdataset));
+ goto failure;
+ }
+
+ for (result = dns_rdataset_first(&rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rdataset)) {
+ bool found = false;
+
+ dns_rdataset_current(&rdataset, &rdata);
+
+ if (kd->all) {
+ if (rdata.length == 5 && rdata.data[0] != 0 &&
+ rdata.data[3] == 0 && rdata.data[4] == 1)
+ found = true;
+ else if (rdata.data[0] == 0 &&
+ (rdata.data[2] & PENDINGFLAGS) != 0) {
+ found = true;
+ clear_pending = true;
+ }
+ } else if (rdata.length == 5 &&
+ memcmp(rdata.data, kd->data, 5) == 0)
+ found = true;
+
+ if (found)
+ CHECK(update_one_rr(db, newver, &diff, DNS_DIFFOP_DEL,
+ &zone->origin, rdataset.ttl,
+ &rdata));
+ dns_rdata_reset(&rdata);
+ }
+
+ if (!ISC_LIST_EMPTY(diff.tuples)) {
+ /* Write changes to journal file. */
+ CHECK(update_soa_serial(db, newver, &diff, zone->mctx,
+ zone->updatemethod));
+
+ result = dns_update_signatures(&log, zone, db,
+ oldver, newver, &diff,
+ zone->sigvalidityinterval);
+ if (!clear_pending)
+ CHECK(result);
+
+ CHECK(zone_journal(zone, &diff, NULL, "keydone"));
+ commit = true;
+
+ LOCK_ZONE(zone);
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED);
+ zone_needdump(zone, 30);
+ UNLOCK_ZONE(zone);
+ }
+
+ failure:
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ if (db != NULL) {
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ if (oldver != NULL)
+ dns_db_closeversion(db, &oldver, false);
+ if (newver != NULL)
+ dns_db_closeversion(db, &newver, commit);
+ dns_db_detach(&db);
+ }
+ dns_diff_clear(&diff);
+ isc_event_free(&event);
+ dns_zone_idetach(&zone);
+
+ INSIST(oldver == NULL);
+ INSIST(newver == NULL);
+}
+
+isc_result_t
+dns_zone_keydone(dns_zone_t *zone, const char *keystr) {
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_event_t *e;
+ isc_buffer_t b;
+ dns_zone_t *dummy = NULL;
+ struct keydone *kd;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+
+ e = isc_event_allocate(zone->mctx, zone, DNS_EVENT_KEYDONE, keydone,
+ zone, sizeof(struct keydone));
+ if (e == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto failure;
+ }
+
+ kd = (struct keydone *) e;
+ if (strcasecmp(keystr, "all") == 0)
+ kd->all = true;
+ else {
+ isc_textregion_t r;
+ const char *algstr;
+ dns_keytag_t keyid;
+ dns_secalg_t alg;
+ size_t n;
+
+ kd->all = false;
+
+ n = sscanf(keystr, "%hu/", &keyid);
+ if (n == 0U)
+ CHECK(ISC_R_FAILURE);
+
+ algstr = strchr(keystr, '/');
+ if (algstr != NULL)
+ algstr++;
+ else
+ CHECK(ISC_R_FAILURE);
+
+ n = sscanf(algstr, "%hhu", &alg);
+ if (n == 0U) {
+ DE_CONST(algstr, r.base);
+ r.length = strlen(algstr);
+ CHECK(dns_secalg_fromtext(&alg, &r));
+ }
+
+ /* construct a private-type rdata */
+ isc_buffer_init(&b, kd->data, sizeof(kd->data));
+ isc_buffer_putuint8(&b, alg);
+ isc_buffer_putuint8(&b, (keyid & 0xff00) >> 8);
+ isc_buffer_putuint8(&b, (keyid & 0xff));
+ isc_buffer_putuint8(&b, 0);
+ isc_buffer_putuint8(&b, 1);
+ }
+
+ zone_iattach(zone, &dummy);
+ isc_task_send(zone->task, &e);
+
+ failure:
+ if (e != NULL)
+ isc_event_free(&e);
+ UNLOCK_ZONE(zone);
+ return (result);
+}
+
+/*
+ * Called from the zone task's queue after the relevant event is posted by
+ * dns_zone_setnsec3param().
+ *
+ * Check whether NSEC3 chain addition or removal specified by the private-type
+ * record passed with the event was already queued (or even fully performed).
+ * If not, modify the relevant private-type records at the zone apex and call
+ * resume_addnsec3chain().
+ */
+static void
+setnsec3param(isc_task_t *task, isc_event_t *event) {
+ const char *me = "setnsec3param";
+ bool commit = false;
+ isc_result_t result;
+ dns_dbversion_t *oldver = NULL, *newver = NULL;
+ dns_zone_t *zone;
+ dns_db_t *db = NULL;
+ dns_dbnode_t *node = NULL;
+ dns_rdataset_t prdataset, nrdataset;
+ dns_diff_t diff;
+ struct np3event *npe = (struct np3event *)event;
+ nsec3param_t *np;
+ dns_update_log_t log = { update_log_cb, NULL };
+ dns_rdata_t rdata;
+ bool nseconly;
+ bool exists = false;
+
+ UNUSED(task);
+
+ zone = event->ev_arg;
+ INSIST(DNS_ZONE_VALID(zone));
+
+ ENTER;
+
+ np = &npe->params;
+
+ dns_rdataset_init(&prdataset);
+ dns_rdataset_init(&nrdataset);
+ dns_diff_init(zone->mctx, &diff);
+
+ 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)
+ goto failure;
+
+ dns_db_currentversion(db, &oldver);
+ result = dns_db_newversion(db, &newver);
+ if (result != ISC_R_SUCCESS) {
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "setnsec3param:dns_db_newversion -> %s",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ CHECK(dns_db_getoriginnode(db, &node));
+
+ /*
+ * Does a private-type record already exist for this chain?
+ */
+ result = dns_db_findrdataset(db, node, newver, zone->privatetype,
+ dns_rdatatype_none, 0, &prdataset, NULL);
+ if (result == ISC_R_SUCCESS) {
+ for (result = dns_rdataset_first(&prdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&prdataset)) {
+ dns_rdata_init(&rdata);
+ dns_rdataset_current(&prdataset, &rdata);
+
+ if (np->length == rdata.length &&
+ memcmp(rdata.data, np->data, np->length) == 0) {
+ exists = true;
+ break;
+ }
+ }
+ } else if (result != ISC_R_NOTFOUND) {
+ INSIST(!dns_rdataset_isassociated(&prdataset));
+ goto failure;
+ }
+
+ /*
+ * Does the chain already exist?
+ */
+ result = dns_db_findrdataset(db, node, newver,
+ dns_rdatatype_nsec3param,
+ dns_rdatatype_none, 0, &nrdataset, NULL);
+ if (result == ISC_R_SUCCESS) {
+ for (result = dns_rdataset_first(&nrdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&nrdataset)) {
+ dns_rdata_init(&rdata);
+ dns_rdataset_current(&nrdataset, &rdata);
+
+ if (np->length == (rdata.length + 1) &&
+ memcmp(rdata.data, np->data + 1,
+ np->length - 1) == 0)
+ {
+ exists = true;
+ break;
+ }
+ }
+ } else if (result != ISC_R_NOTFOUND) {
+ INSIST(!dns_rdataset_isassociated(&nrdataset));
+ goto failure;
+ }
+
+
+ /*
+ * We need to remove any existing NSEC3 chains if the supplied NSEC3
+ * parameters are supposed to replace the current ones or if we are
+ * switching to NSEC.
+ */
+ if (!exists && np->replace && (np->length != 0 || np->nsec))
+ CHECK(dns_nsec3param_deletechains(db, newver, zone,
+ !np->nsec, &diff));
+
+ if (!exists && np->length != 0) {
+ /*
+ * We're creating an NSEC3 chain. Add the private-type record
+ * passed in the event handler's argument to the zone apex.
+ *
+ * If the zone is not currently capable of supporting an NSEC3
+ * chain (due to the DNSKEY RRset at the zone apex not existing
+ * or containing at least one key using an NSEC-only
+ * algorithm), add the INITIAL flag, so these parameters can be
+ * used later when NSEC3 becomes available.
+ */
+ dns_rdata_init(&rdata);
+
+ np->data[2] |= DNS_NSEC3FLAG_CREATE;
+ result = dns_nsec_nseconly(db, newver, &nseconly);
+ if (result == ISC_R_NOTFOUND || nseconly)
+ np->data[2] |= DNS_NSEC3FLAG_INITIAL;
+
+ rdata.length = np->length;
+ rdata.data = np->data;
+ rdata.type = zone->privatetype;
+ rdata.rdclass = zone->rdclass;
+ CHECK(update_one_rr(db, newver, &diff, DNS_DIFFOP_ADD,
+ &zone->origin, 0, &rdata));
+ }
+
+ /*
+ * If we changed anything in the zone, write changes to journal file
+ * and set commit to true so that resume_addnsec3chain() will be
+ * called below in order to kick off adding/removing relevant NSEC3
+ * records.
+ */
+ if (!ISC_LIST_EMPTY(diff.tuples)) {
+ CHECK(update_soa_serial(db, newver, &diff, zone->mctx,
+ zone->updatemethod));
+ result = dns_update_signatures(&log, zone, db,
+ oldver, newver, &diff,
+ zone->sigvalidityinterval);
+ if (result != ISC_R_NOTFOUND)
+ CHECK(result);
+ CHECK(zone_journal(zone, &diff, NULL, "setnsec3param"));
+ commit = true;
+
+ LOCK_ZONE(zone);
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED);
+ zone_needdump(zone, 30);
+ UNLOCK_ZONE(zone);
+ }
+
+ failure:
+ if (dns_rdataset_isassociated(&prdataset))
+ dns_rdataset_disassociate(&prdataset);
+ if (dns_rdataset_isassociated(&nrdataset))
+ dns_rdataset_disassociate(&nrdataset);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ if (oldver != NULL)
+ dns_db_closeversion(db, &oldver, false);
+ if (newver != NULL)
+ dns_db_closeversion(db, &newver, commit);
+ if (db != NULL)
+ dns_db_detach(&db);
+ if (commit) {
+ LOCK_ZONE(zone);
+ resume_addnsec3chain(zone);
+ UNLOCK_ZONE(zone);
+ }
+ dns_diff_clear(&diff);
+ isc_event_free(&event);
+ dns_zone_idetach(&zone);
+
+ INSIST(oldver == NULL);
+ INSIST(newver == NULL);
+}
+
+/*
+ * Called when an "rndc signing -nsec3param ..." command is received.
+ *
+ * Allocate and prepare an nsec3param_t structure which holds information about
+ * the NSEC3 changes requested for the zone:
+ *
+ * - if NSEC3 is to be disabled ("-nsec3param none"), only set the "nsec"
+ * field of the structure to true and the "replace" field to the value
+ * of the "replace" argument, leaving other fields initialized to zeros, to
+ * signal that the zone should be signed using NSEC instead of NSEC3,
+ *
+ * - otherwise, prepare NSEC3PARAM RDATA that will eventually be inserted at
+ * the zone apex, convert it to a private-type record and store the latter
+ * in the "data" field of the nsec3param_t structure.
+ *
+ * Once the nsec3param_t structure is prepared, post an event to the zone's
+ * task which will cause setnsec3param() to be called with the prepared
+ * structure passed as an argument.
+ */
+isc_result_t
+dns_zone_setnsec3param(dns_zone_t *zone, uint8_t hash, uint8_t flags,
+ uint16_t iter, uint8_t saltlen,
+ unsigned char *salt, bool replace)
+{
+ isc_result_t result = ISC_R_SUCCESS;
+ dns_rdata_nsec3param_t param;
+ dns_rdata_t nrdata = DNS_RDATA_INIT;
+ dns_rdata_t prdata = DNS_RDATA_INIT;
+ unsigned char nbuf[DNS_NSEC3PARAM_BUFFERSIZE];
+ struct np3event *npe;
+ nsec3param_t *np;
+ dns_zone_t *dummy = NULL;
+ isc_buffer_t b;
+ isc_event_t *e;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(salt != NULL);
+
+ LOCK_ZONE(zone);
+
+ e = isc_event_allocate(zone->mctx, zone, DNS_EVENT_SETNSEC3PARAM,
+ setnsec3param, zone, sizeof(struct np3event));
+ if (e == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto failure;
+ }
+
+ npe = (struct np3event *) e;
+ np = &npe->params;
+
+ np->replace = replace;
+ if (hash == 0) {
+ np->length = 0;
+ np->nsec = true;
+ } else {
+ param.common.rdclass = zone->rdclass;
+ param.common.rdtype = dns_rdatatype_nsec3param;
+ ISC_LINK_INIT(&param.common, link);
+ param.mctx = NULL;
+ param.hash = hash;
+ param.flags = flags;
+ param.iterations = iter;
+ param.salt_length = saltlen;
+ param.salt = salt;
+ isc_buffer_init(&b, nbuf, sizeof(nbuf));
+ CHECK(dns_rdata_fromstruct(&nrdata, zone->rdclass,
+ dns_rdatatype_nsec3param,
+ &param, &b));
+ dns_nsec3param_toprivate(&nrdata, &prdata, zone->privatetype,
+ np->data, sizeof(np->data));
+ np->length = prdata.length;
+ np->nsec = false;
+ }
+
+ /*
+ * setnsec3param() will silently return early if the zone does not yet
+ * have a database. Prevent that by queueing the event up if zone->db
+ * is NULL. All events queued here are subsequently processed by
+ * receive_secure_db() if it ever gets called or simply freed by
+ * zone_free() otherwise.
+ */
+ ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read);
+ if (zone->db != NULL) {
+ zone_iattach(zone, &dummy);
+ isc_task_send(zone->task, &e);
+ } else {
+ ISC_LIST_APPEND(zone->setnsec3param_queue, e, ev_link);
+ e = NULL;
+ }
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
+
+ failure:
+ if (e != NULL)
+ isc_event_free(&e);
+ UNLOCK_ZONE(zone);
+ return (result);
+}
+
+isc_result_t
+dns_zone_getloadtime(dns_zone_t *zone, isc_time_t *loadtime) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(loadtime != NULL);
+
+ LOCK_ZONE(zone);
+ *loadtime = zone->loadtime;
+ UNLOCK_ZONE(zone);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_zone_getexpiretime(dns_zone_t *zone, isc_time_t *expiretime) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(expiretime != NULL);
+
+ LOCK_ZONE(zone);
+ *expiretime = zone->expiretime;
+ UNLOCK_ZONE(zone);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_zone_getrefreshtime(dns_zone_t *zone, isc_time_t *refreshtime) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(refreshtime != NULL);
+
+ LOCK_ZONE(zone);
+ *refreshtime = zone->refreshtime;
+ UNLOCK_ZONE(zone);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_zone_getrefreshkeytime(dns_zone_t *zone, isc_time_t *refreshkeytime) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(refreshkeytime != NULL);
+
+ LOCK_ZONE(zone);
+ *refreshkeytime = zone->refreshkeytime;
+ UNLOCK_ZONE(zone);
+ return (ISC_R_SUCCESS);
+}
+
+unsigned int
+dns_zone_getincludes(dns_zone_t *zone, char ***includesp) {
+ dns_include_t *include;
+ char **array = NULL;
+ unsigned int n = 0;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(includesp != NULL && *includesp == NULL);
+
+ LOCK_ZONE(zone);
+ if (zone->nincludes == 0)
+ goto done;
+
+ array = isc_mem_allocate(zone->mctx, sizeof(char *) * zone->nincludes);
+ if (array == NULL)
+ goto done;
+ for (include = ISC_LIST_HEAD(zone->includes);
+ include != NULL;
+ include = ISC_LIST_NEXT(include, link)) {
+ INSIST(n < zone->nincludes);
+ array[n++] = isc_mem_strdup(zone->mctx, include->name);
+ }
+ INSIST(n == zone->nincludes);
+ *includesp = array;
+
+ done:
+ UNLOCK_ZONE(zone);
+ return (n);
+}
+
+void
+dns_zone_setstatlevel(dns_zone_t *zone, dns_zonestat_level_t level) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ zone->statlevel = level;
+}
+
+dns_zonestat_level_t
+dns_zone_getstatlevel(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return (zone->statlevel);
+}
+
+static void
+setserial(isc_task_t *task, isc_event_t *event) {
+ uint32_t oldserial, desired;
+ const char *me = "setserial";
+ bool commit = false;
+ isc_result_t result;
+ dns_dbversion_t *oldver = NULL, *newver = NULL;
+ dns_zone_t *zone;
+ dns_db_t *db = NULL;
+ dns_diff_t diff;
+ struct ssevent *sse = (struct ssevent *)event;
+ dns_update_log_t log = { update_log_cb, NULL };
+ dns_difftuple_t *oldtuple = NULL, *newtuple = NULL;
+
+ UNUSED(task);
+
+ zone = event->ev_arg;
+ INSIST(DNS_ZONE_VALID(zone));
+
+ ENTER;
+
+ if (zone->update_disabled)
+ goto failure;
+
+ desired = sse->serial;
+
+ dns_diff_init(zone->mctx, &diff);
+
+ 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)
+ goto failure;
+
+ dns_db_currentversion(db, &oldver);
+ result = dns_db_newversion(db, &newver);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "setserial:dns_db_newversion -> %s",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ CHECK(dns_db_createsoatuple(db, oldver, diff.mctx,
+ DNS_DIFFOP_DEL, &oldtuple));
+ CHECK(dns_difftuple_copy(oldtuple, &newtuple));
+ newtuple->op = DNS_DIFFOP_ADD;
+
+ oldserial = dns_soa_getserial(&oldtuple->rdata);
+ if (desired == 0U)
+ desired = 1;
+ if (!isc_serial_gt(desired, oldserial)) {
+ if (desired != oldserial)
+ dns_zone_log(zone, ISC_LOG_INFO,
+ "setserial: desired serial (%u) "
+ "out of range (%u-%u)", desired,
+ oldserial + 1, (oldserial + 0x7fffffff));
+ goto failure;
+ }
+
+ dns_soa_setserial(desired, &newtuple->rdata);
+ CHECK(do_one_tuple(&oldtuple, db, newver, &diff));
+ CHECK(do_one_tuple(&newtuple, db, newver, &diff));
+ result = dns_update_signatures(&log, zone, db,
+ oldver, newver, &diff,
+ zone->sigvalidityinterval);
+ if (result != ISC_R_NOTFOUND)
+ CHECK(result);
+
+ /* Write changes to journal file. */
+ CHECK(zone_journal(zone, &diff, NULL, "setserial"));
+ commit = true;
+
+ LOCK_ZONE(zone);
+ zone_needdump(zone, 30);
+ UNLOCK_ZONE(zone);
+
+ failure:
+ if (oldtuple != NULL)
+ dns_difftuple_free(&oldtuple);
+ if (newtuple != NULL)
+ dns_difftuple_free(&newtuple);
+ if (oldver != NULL)
+ dns_db_closeversion(db, &oldver, false);
+ if (newver != NULL)
+ dns_db_closeversion(db, &newver, commit);
+ if (db != NULL)
+ dns_db_detach(&db);
+ dns_diff_clear(&diff);
+ isc_event_free(&event);
+ dns_zone_idetach(&zone);
+
+ INSIST(oldver == NULL);
+ INSIST(newver == NULL);
+}
+
+isc_result_t
+dns_zone_setserial(dns_zone_t *zone, uint32_t serial) {
+ isc_result_t result = ISC_R_SUCCESS;
+ dns_zone_t *dummy = NULL;
+ isc_event_t *e = NULL;
+ struct ssevent *sse;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+
+ if (!inline_secure(zone)) {
+ if (!dns_zone_isdynamic(zone, true)) {
+ result = DNS_R_NOTDYNAMIC;
+ goto failure;
+ }
+ }
+
+ if (zone->update_disabled) {
+ result = DNS_R_FROZEN;
+ goto failure;
+ }
+
+ e = isc_event_allocate(zone->mctx, zone, DNS_EVENT_SETSERIAL,
+ setserial, zone, sizeof(struct ssevent));
+ if (e == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto failure;
+ }
+
+ sse = (struct ssevent *)e;
+ sse->serial = serial;
+
+ zone_iattach(zone, &dummy);
+ isc_task_send(zone->task, &e);
+
+ failure:
+ if (e != NULL)
+ isc_event_free(&e);
+ UNLOCK_ZONE(zone);
+ return (result);
+}
diff --git a/lib/dns/zone_p.h b/lib/dns/zone_p.h
new file mode 100644
index 0000000..1f4432a
--- /dev/null
+++ b/lib/dns/zone_p.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef DNS_ZONE_P_H
+#define DNS_ZONE_P_H
+
+#include <stdbool.h>
+
+/*! \file */
+
+/*%
+ * Types and functions below not be used outside this module and its
+ * associated unit tests.
+ */
+
+ISC_LANG_BEGINDECLS
+
+typedef struct {
+ dns_diff_t *diff;
+ bool offline;
+} dns__zonediff_t;
+
+isc_result_t
+dns__zone_findkeys(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver,
+ isc_stdtime_t now, isc_mem_t *mctx, unsigned int maxkeys,
+ dst_key_t **keys, unsigned int *nkeys);
+
+isc_result_t
+dns__zone_updatesigs(dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *version,
+ dst_key_t *zone_keys[], unsigned int nkeys,
+ dns_zone_t *zone, isc_stdtime_t inception,
+ isc_stdtime_t expire, isc_stdtime_t now,
+ bool check_ksk, bool keyset_kskonly,
+ dns__zonediff_t *zonediff);
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_ZONE_P_H */
diff --git a/lib/dns/zonekey.c b/lib/dns/zonekey.c
new file mode 100644
index 0000000..ca55520
--- /dev/null
+++ b/lib/dns/zonekey.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdbool.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>
+
+bool
+dns_zonekey_iszonekey(dns_rdata_t *keyrdata) {
+ isc_result_t result;
+ dns_rdata_dnskey_t key;
+ bool iszonekey = true;
+
+ REQUIRE(keyrdata != NULL);
+
+ result = dns_rdata_tostruct(keyrdata, &key, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (false);
+
+ if ((key.flags & DNS_KEYTYPE_NOAUTH) != 0)
+ iszonekey = false;
+ if ((key.flags & DNS_KEYFLAG_OWNERMASK) != DNS_KEYOWNER_ZONE)
+ iszonekey = false;
+ if (key.protocol != DNS_KEYPROTO_DNSSEC &&
+ key.protocol != DNS_KEYPROTO_ANY)
+ iszonekey = false;
+
+ return (iszonekey);
+}
diff --git a/lib/dns/zt.c b/lib/dns/zt.c
new file mode 100644
index 0000000..c84da2f
--- /dev/null
+++ b/lib/dns/zt.c
@@ -0,0 +1,601 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/file.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/string.h>
+#include <isc/task.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;
+ dns_zt_allloaded_t loaddone;
+ void * loaddone_arg;
+ /* Locked by lock. */
+ bool flush;
+ uint32_t references;
+ unsigned int loads_pending;
+ dns_rbt_t *table;
+};
+
+#define ZTMAGIC ISC_MAGIC('Z', 'T', 'b', 'l')
+#define VALID_ZT(zt) ISC_MAGIC_VALID(zt, ZTMAGIC)
+
+struct zt_load_params {
+ dns_zt_zoneloaded_t dl;
+ bool newonly;
+ struct dns_zt* zt;
+};
+
+static void
+auto_detach(void *, void *);
+
+static isc_result_t
+load(dns_zone_t *zone, void *uap);
+
+static isc_result_t
+asyncload(dns_zone_t *zone, void *callback);
+
+static isc_result_t
+loadnew(dns_zone_t *zone, void *uap);
+
+static isc_result_t
+freezezones(dns_zone_t *zone, void *uap);
+
+static isc_result_t
+doneloading(dns_zt_t *zt, dns_zone_t *zone, isc_task_t *task);
+
+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 = NULL;
+ isc_mem_attach(mctx, &zt->mctx);
+ zt->references = 1;
+ zt->flush = false;
+ zt->rdclass = rdclass;
+ zt->magic = ZTMAGIC;
+ zt->loaddone = NULL;
+ zt->loaddone_arg = NULL;
+ zt->loads_pending = 0;
+ *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, false);
+
+ RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);
+
+ return (result);
+}
+
+isc_result_t
+dns_zt_find(dns_zt_t *zt, const 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_destroy(dns_zt_t *zt) {
+ if (zt->flush)
+ (void)dns_zt_apply(zt, false, flush, NULL);
+ dns_rbt_destroy(&zt->table);
+ isc_rwlock_destroy(&zt->rwlock);
+ zt->magic = 0;
+ isc_mem_putanddetach(&zt->mctx, zt, sizeof(*zt));
+}
+
+static void
+zt_flushanddetach(dns_zt_t **ztp, bool need_flush) {
+ bool destroy = 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 = true;
+ if (need_flush)
+ zt->flush = true;
+
+ RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);
+
+ if (destroy)
+ zt_destroy(zt);
+
+ *ztp = NULL;
+}
+
+void
+dns_zt_flushanddetach(dns_zt_t **ztp) {
+ zt_flushanddetach(ztp, true);
+}
+
+void
+dns_zt_detach(dns_zt_t **ztp) {
+ zt_flushanddetach(ztp, false);
+}
+
+isc_result_t
+dns_zt_load(dns_zt_t *zt, bool 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_asyncload(dns_zt_t *zt, dns_zt_allloaded_t alldone, void *arg) {
+ return (dns_zt_asyncload2(zt, alldone, arg, false));
+}
+
+isc_result_t
+dns_zt_asyncload2(dns_zt_t *zt, dns_zt_allloaded_t alldone, void *arg,
+ bool newonly)
+{
+ isc_result_t result;
+ struct zt_load_params params;
+ params.dl = doneloading;
+ params.newonly = newonly;
+ params.zt = zt;
+ int pending;
+
+ REQUIRE(VALID_ZT(zt));
+
+ RWLOCK(&zt->rwlock, isc_rwlocktype_write);
+
+ INSIST(zt->loads_pending == 0);
+ result = dns_zt_apply2(zt, false, NULL, asyncload, &params);
+
+ pending = zt->loads_pending;
+ if (pending != 0) {
+ zt->loaddone = alldone;
+ zt->loaddone_arg = arg;
+ }
+
+ RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);
+
+ if (pending == 0)
+ alldone(arg);
+
+ return (result);
+}
+
+/*
+ * Initiates asynchronous loading of zone 'zone'. 'callback' is a
+ * pointer to a function which will be used to inform the caller when
+ * the zone loading is complete.
+ */
+static isc_result_t
+asyncload(dns_zone_t *zone, void *paramsv) {
+ isc_result_t result;
+ struct zt_load_params * params = (struct zt_load_params*) paramsv;
+
+ dns_zt_t *zt = params->zt;
+
+ REQUIRE(zone != NULL);
+ zt = dns_zone_getview(zone)->zonetable;
+ INSIST(VALID_ZT(zt));
+
+ INSIST(zt->references > 0);
+ zt->references++;
+ zt->loads_pending++;
+
+ result = dns_zone_asyncload2(zone, *params->dl, zt, params->newonly);
+ if (result != ISC_R_SUCCESS) {
+ zt->references--;
+ zt->loads_pending--;
+ INSIST(zt->references > 0);
+ }
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_zt_loadnew(dns_zt_t *zt, bool 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, bool freeze) {
+ isc_result_t result, tresult;
+
+ REQUIRE(VALID_ZT(zt));
+
+ RWLOCK(&zt->rwlock, isc_rwlocktype_read);
+ result = dns_zt_apply2(zt, false, &tresult, freezezones, &freeze);
+ RWUNLOCK(&zt->rwlock, isc_rwlocktype_read);
+ if (tresult == ISC_R_NOTFOUND)
+ tresult = ISC_R_SUCCESS;
+ return ((result == ISC_R_SUCCESS) ? tresult : result);
+}
+
+static isc_result_t
+freezezones(dns_zone_t *zone, void *uap) {
+ bool freeze = *(bool *)uap;
+ bool frozen;
+ isc_result_t result = ISC_R_SUCCESS;
+ char classstr[DNS_RDATACLASS_FORMATSIZE];
+ char zonename[DNS_NAME_FORMATSIZE];
+ dns_zone_t *raw = NULL;
+ dns_view_t *view;
+ const char *vname;
+ const char *sep;
+ int level;
+
+ dns_zone_getraw(zone, &raw);
+ if (raw != NULL)
+ zone = raw;
+ if (dns_zone_gettype(zone) != dns_zone_master) {
+ if (raw != NULL)
+ dns_zone_detach(&raw);
+ return (ISC_R_SUCCESS);
+ }
+ if (!dns_zone_isdynamic(zone, true)) {
+ if (raw != NULL)
+ dns_zone_detach(&raw);
+ 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)
+ dns_zone_setupdatedisabled(zone, freeze);
+ } else {
+ if (frozen) {
+ result = dns_zone_loadandthaw(zone);
+ if (result == DNS_R_CONTINUE ||
+ result == DNS_R_UPTODATE)
+ result = ISC_R_SUCCESS;
+ }
+ }
+ 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));
+ if (raw != NULL)
+ dns_zone_detach(&raw);
+ return (result);
+}
+
+void
+dns_zt_setviewcommit(dns_zt_t *zt) {
+ dns_rbtnode_t *node;
+ dns_rbtnodechain_t chain;
+ isc_result_t result;
+
+ REQUIRE(VALID_ZT(zt));
+
+ dns_rbtnodechain_init(&chain, zt->mctx);
+
+ result = dns_rbtnodechain_first(&chain, zt->table, NULL, NULL);
+ while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) {
+ result = dns_rbtnodechain_current(&chain, NULL, NULL,
+ &node);
+ if (result == ISC_R_SUCCESS && node->data != NULL) {
+ dns_zone_setviewcommit(node->data);
+ }
+
+ result = dns_rbtnodechain_next(&chain, NULL, NULL);
+ }
+
+ dns_rbtnodechain_invalidate(&chain);
+}
+
+void
+dns_zt_setviewrevert(dns_zt_t *zt) {
+ dns_rbtnode_t *node;
+ dns_rbtnodechain_t chain;
+ isc_result_t result;
+
+ REQUIRE(VALID_ZT(zt));
+
+ dns_rbtnodechain_init(&chain, zt->mctx);
+
+ result = dns_rbtnodechain_first(&chain, zt->table, NULL, NULL);
+ while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) {
+ result = dns_rbtnodechain_current(&chain, NULL, NULL,
+ &node);
+ if (result == ISC_R_SUCCESS && node->data != NULL) {
+ dns_zone_setviewrevert(node->data);
+ }
+
+ result = dns_rbtnodechain_next(&chain, NULL, NULL);
+ }
+
+ dns_rbtnodechain_invalidate(&chain);
+}
+
+isc_result_t
+dns_zt_apply(dns_zt_t *zt, bool 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, bool 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.
+ */
+ tresult = result;
+ 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);
+}
+
+/*
+ * Decrement the loads_pending counter; when counter reaches
+ * zero, call the loaddone callback that was initially set by
+ * dns_zt_asyncload().
+ */
+static isc_result_t
+doneloading(dns_zt_t *zt, dns_zone_t *zone, isc_task_t *task) {
+ bool destroy = false;
+ dns_zt_allloaded_t alldone = NULL;
+ void *arg = NULL;
+
+ UNUSED(zone);
+ UNUSED(task);
+
+ REQUIRE(VALID_ZT(zt));
+
+ RWLOCK(&zt->rwlock, isc_rwlocktype_write);
+ INSIST(zt->loads_pending != 0);
+ INSIST(zt->references != 0);
+ zt->references--;
+ if (zt->references == 0)
+ destroy = true;
+ zt->loads_pending--;
+ if (zt->loads_pending == 0) {
+ alldone = zt->loaddone;
+ arg = zt->loaddone_arg;
+ zt->loaddone = NULL;
+ zt->loaddone_arg = NULL;
+ }
+ RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);
+
+ if (alldone != NULL)
+ alldone(arg);
+
+ if (destroy)
+ zt_destroy(zt);
+
+ return (ISC_R_SUCCESS);
+}
+
+/***
+ *** Private
+ ***/
+
+static void
+auto_detach(void *data, void *arg) {
+ dns_zone_t *zone = data;
+
+ UNUSED(arg);
+ dns_zone_detach(&zone);
+}